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 void prependEnvPath(string const & name, string const & prefix)
433 vector<string> env_var = getEnvPath(name);
435 typedef boost::char_separator<char> Separator;
436 typedef boost::tokenizer<Separator> Tokenizer;
438 Separator const separator(string(1, os::path_separator()).c_str());
440 // Prepend each new element to the list, removing identical elements
441 // that occur later in the list.
442 Tokenizer const tokens(prefix, separator);
443 vector<string> reversed_tokens(tokens.begin(), tokens.end());
445 typedef vector<string>::const_reverse_iterator token_iterator;
446 token_iterator it = reversed_tokens.rbegin();
447 token_iterator const end = reversed_tokens.rend();
448 for (; it != end; ++it) {
449 vector<string>::iterator remove_it =
450 std::remove(env_var.begin(), env_var.end(), *it);
451 env_var.erase(remove_it, env_var.end());
452 env_var.insert(env_var.begin(), *it);
455 setEnvPath(name, env_var);
459 bool putEnv(string const & envstr)
461 // CHECK Look at and fix this.
462 // f.ex. what about error checking?
464 #if defined (HAVE_SETENV)
466 string const value = split(envstr, name, '=');
467 int const retval = ::setenv(name.c_str(), value.c_str(), true);
468 #elif defined (HAVE_PUTENV)
469 // this leaks, but what can we do about it?
470 // Is doing a getenv() and a free() of the older value
471 // a good idea? (JMarc)
472 // Actually we don't have to leak...calling putenv like this
473 // should be enough: ... and this is obviously not enough if putenv
474 // does not make a copy of the string. It is also not very wise to
475 // put a string on the free store. If we have to leak we should do it
477 char * leaker = new char[envstr.length() + 1];
478 envstr.copy(leaker, envstr.length());
479 leaker[envstr.length()] = '\0';
480 int const retval = ::putenv(leaker);
482 // If putenv does not make a copy of the char const * this
483 // is very dangerous. OTOH if it does take a copy this is the
485 // The only implementation of putenv that I have seen does not
486 // allocate memory. _And_ after testing the putenv in glibc it
487 // seems that we need to make a copy of the string contents.
488 // I will enable the above.
489 //int retval = lyx::putenv(envstr.c_str());
491 // No environment setting function. Can this happen?
492 int const retval = 1; //return an error condition.
500 int DeleteAllFilesInDir(string const & path)
502 // I have decided that we will be using parts from the boost
503 // library. Check out http://www.boost.org/
504 // For directory access we will then use the directory_iterator.
505 // Then the code will be something like:
506 // directory_iterator dit(path);
507 // directory_iterator dend;
508 // if (dit == dend) {
511 // for (; dit != dend; ++dit) {
512 // string filename(*dit);
513 // if (filename == "." || filename == "..")
515 // string unlinkpath(AddName(path, filename));
516 // lyx::unlink(unlinkpath);
519 DIR * dir = ::opendir(path.c_str());
524 int return_value = 0;
525 while ((de = readdir(dir))) {
526 string const temp = de->d_name;
527 if (temp == "." || temp == "..")
529 string const unlinkpath = AddName (path, temp);
531 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
535 FileInfo fi(unlinkpath);
536 if (fi.isOK() && fi.isDir()) {
537 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
538 deleted &= (rmdir(unlinkpath) == 0);
540 deleted &= (unlink(unlinkpath) == 0);
549 string const createTmpDir(string const & tempdir, string const & mask)
552 << "createTmpDir: tempdir=`" << tempdir << "'\n"
553 << "createTmpDir: mask=`" << mask << '\'' << endl;
555 string const tmpfl(tempName(tempdir, mask));
556 // lyx::tempName actually creates a file to make sure that it
557 // stays unique. So we have to delete it before we can create
558 // a dir with the same name. Note also that we are not thread
559 // safe because of the gap between unlink and mkdir. (Lgb)
562 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
563 lyxerr << "LyX could not create the temporary directory '"
564 << tmpfl << "'" << endl;
568 return MakeAbsPath(tmpfl);
574 int destroyDir(string const & tmpdir)
577 Path p(user_lyxdir());
579 if (DeleteAllFilesInDir(tmpdir))
589 string const createBufferTmpDir()
592 // We are in our own directory. Why bother to mangle name?
593 // In fact I wrote this code to circumvent a problematic behaviour
594 // (bug?) of EMX mkstemp().
596 package().temp_dir() + "/lyx_tmpbuf" +
597 convert<string>(count++);
599 if (mkdir(tmpfl, 0777)) {
600 lyxerr << "LyX could not create the temporary directory '"
601 << tmpfl << "'" << endl;
608 string const createLyXTmpDir(string const & deflt)
610 if (!deflt.empty() && deflt != "/tmp") {
611 if (mkdir(deflt, 0777)) {
613 Path p(package().user_support());
615 if (IsDirWriteable(deflt)) {
616 // deflt could not be created because it
617 // did exist already, so let's create our own
619 return createTmpDir(deflt, "lyx_tmpdir");
621 // some other error occured.
622 return createTmpDir("/tmp", "lyx_tmpdir");
628 Path p(package().user_support());
630 return createTmpDir("/tmp", "lyx_tmpdir");
635 bool createDirectory(string const & path, int permission)
637 string temp(rtrim(os::internal_path(path), "/"));
639 BOOST_ASSERT(!temp.empty());
641 if (mkdir(temp, permission))
648 // Strip filename from path name
649 string const OnlyPath(string const & Filename)
651 // If empty filename, return empty
652 if (Filename.empty()) return Filename;
654 // Find last / or start of filename
655 string::size_type j = Filename.rfind('/');
656 if (j == string::npos)
658 return Filename.substr(0, j + 1);
662 // Convert relative path into absolute path based on a basepath.
663 // If relpath is absolute, just use that.
664 // If basepath is empty, use CWD as base.
665 string const MakeAbsPath(string const & RelPath, string const & BasePath)
667 // checks for already absolute path
668 if (os::is_absolute_path(RelPath))
671 // Copies given paths
672 string TempRel(os::internal_path(RelPath));
673 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
674 TempRel = subst(TempRel, "//", "/");
678 if (os::is_absolute_path(BasePath))
681 TempBase = AddPath(getcwd(), BasePath);
683 // Handle /./ at the end of the path
684 while (suffixIs(TempBase, "/./"))
685 TempBase.erase(TempBase.length() - 2);
687 // processes relative path
688 string RTemp(TempRel);
691 while (!RTemp.empty()) {
693 RTemp = split(RTemp, Temp, '/');
695 if (Temp == ".") continue;
697 // Remove one level of TempBase
698 string::difference_type i = TempBase.length() - 2;
701 while (i > 0 && TempBase[i] != '/') --i;
705 while (i > 2 && TempBase[i] != '/') --i;
708 TempBase.erase(i, string::npos);
711 } else if (Temp.empty() && !RTemp.empty()) {
712 TempBase = os::current_root() + RTemp;
715 // Add this piece to TempBase
716 if (!suffixIs(TempBase, '/'))
722 // returns absolute path
723 return os::internal_path(TempBase);
727 // Correctly append filename to the pathname.
728 // If pathname is '.', then don't use pathname.
729 // Chops any path of filename.
730 string const AddName(string const & path, string const & fname)
733 string const basename(OnlyFilename(fname));
737 if (path != "." && path != "./" && !path.empty()) {
738 buf = os::internal_path(path);
739 if (!suffixIs(path, '/'))
743 return buf + basename;
747 // Strips path from filename
748 string const OnlyFilename(string const & fname)
753 string::size_type j = fname.rfind('/');
754 if (j == string::npos) // no '/' in fname
758 return fname.substr(j + 1);
762 /// Returns true is path is absolute
763 bool AbsolutePath(string const & path)
765 return os::is_absolute_path(path);
770 // Create absolute path. If impossible, don't do anything
771 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
772 string const ExpandPath(string const & path)
774 // checks for already absolute path
775 string RTemp(ReplaceEnvironmentPath(path));
776 if (os::is_absolute_path(RTemp))
780 string const copy(RTemp);
783 RTemp = split(RTemp, Temp, '/');
786 return getcwd() + '/' + RTemp;
789 return package().home_dir() + '/' + RTemp;
792 return MakeAbsPath(copy);
794 // Don't know how to handle this
800 // Constracts path/../path
801 // Can't handle "../../" or "/../" (Asger)
802 // Also converts paths like /foo//bar ==> /foo/bar
803 string const NormalizePath(string const & path)
809 if (os::is_absolute_path(path))
812 // Make implicit current directory explicit
815 // Normalise paths like /foo//bar ==> /foo/bar
816 boost::RegEx regex("/{2,}");
817 RTemp = regex.Merge(RTemp, "/");
819 while (!RTemp.empty()) {
821 RTemp = split(RTemp, Temp, '/');
825 } else if (Temp == "..") {
826 // Remove one level of TempBase
827 string::difference_type i = TempBase.length() - 2;
828 while (i > 0 && TempBase[i] != '/')
830 if (i >= 0 && TempBase[i] == '/')
831 TempBase.erase(i + 1, string::npos);
835 TempBase += Temp + '/';
839 // returns absolute path
844 string const GetFileContents(string const & fname)
846 FileInfo finfo(fname);
848 ifstream ifs(fname.c_str());
856 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
861 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
862 string const ReplaceEnvironmentPath(string const & path)
864 // ${VAR} is defined as
865 // $\{[A-Za-z_][A-Za-z_0-9]*\}
866 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
868 // $VAR is defined as:
869 // $\{[A-Za-z_][A-Za-z_0-9]*\}
870 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
872 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
873 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
876 string result = path;
878 regex_match(result, what, envvar_br_re);
879 if (!what[0].matched) {
880 regex_match(result, what, envvar_re);
881 if (!what[0].matched)
884 result = what.str(1) + GetEnv(what.str(2)) + what.str(3);
890 // Make relative path out of two absolute paths
891 string const MakeRelPath(string const & abspath, string const & basepath)
892 // Makes relative path out of absolute path. If it is deeper than basepath,
893 // it's easy. If basepath and abspath share something (they are all deeper
894 // than some directory), it'll be rendered using ..'s. If they are completely
895 // different, then the absolute path will be used as relative path.
897 string::size_type const abslen = abspath.length();
898 string::size_type const baselen = basepath.length();
900 string::size_type i = os::common_path(abspath, basepath);
903 // actually no match - cannot make it relative
907 // Count how many dirs there are in basepath above match
908 // and append as many '..''s into relpath
910 string::size_type j = i;
911 while (j < baselen) {
912 if (basepath[j] == '/') {
913 if (j + 1 == baselen)
920 // Append relative stuff from common directory to abspath
921 if (abspath[i] == '/')
923 for (; i < abslen; ++i)
926 if (suffixIs(buf, '/'))
927 buf.erase(buf.length() - 1);
928 // Substitute empty with .
935 // Append sub-directory(ies) to a path in an intelligent way
936 string const AddPath(string const & path, string const & path_2)
939 string const path2 = os::internal_path(path_2);
941 if (!path.empty() && path != "." && path != "./") {
942 buf = os::internal_path(path);
943 if (path[path.length() - 1] != '/')
947 if (!path2.empty()) {
948 string::size_type const p2start = path2.find_first_not_of('/');
949 string::size_type const p2end = path2.find_last_not_of('/');
950 string const tmp = path2.substr(p2start, p2end - p2start + 1);
958 Change extension of oldname to extension.
959 Strips path off if no_path == true.
960 If no extension on oldname, just appends.
962 string const ChangeExtension(string const & oldname, string const & extension)
964 string::size_type const last_slash = oldname.rfind('/');
965 string::size_type last_dot = oldname.rfind('.');
966 if (last_dot < last_slash && last_slash != string::npos)
967 last_dot = string::npos;
970 // Make sure the extension starts with a dot
971 if (!extension.empty() && extension[0] != '.')
972 ext= '.' + extension;
976 return os::internal_path(oldname.substr(0, last_dot) + ext);
980 /// Return the extension of the file (not including the .)
981 string const GetExtension(string const & name)
983 string::size_type const last_slash = name.rfind('/');
984 string::size_type const last_dot = name.rfind('.');
985 if (last_dot != string::npos &&
986 (last_slash == string::npos || last_dot > last_slash))
987 return name.substr(last_dot + 1,
988 name.length() - (last_dot + 1));
994 // the different filetypes and what they contain in one of the first lines
995 // (dots are any characters). (Herbert 20020131)
998 // EPS %!PS-Adobe-3.0 EPSF...
1000 // FITS ...BITPIX...
1005 // PBM P1... or P4 (B/W)
1006 // PGM P2... or P5 (Grayscale)
1007 // PPM P3... or P6 (color)
1008 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
1009 // SGI \001\332... (decimal 474)
1011 // TIFF II... or MM...
1012 // XBM ..._bits[]...
1013 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
1014 // ...static char *...
1015 // XWD \000\000\000\151 (0x00006900) decimal 105
1017 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
1018 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
1019 // Z \037\235 UNIX compress
1021 string const getFormatFromContents(string const & filename)
1024 if (filename.empty() || !IsFileReadable(filename))
1027 ifstream ifs(filename.c_str());
1029 // Couldn't open file...
1033 string const gzipStamp = "\037\213";
1036 string const zipStamp = "PK";
1039 string const compressStamp = "\037\235";
1041 // Maximum strings to read
1042 int const max_count = 50;
1047 bool firstLine = true;
1048 while ((count++ < max_count) && format.empty()) {
1050 lyxerr[Debug::GRAPHICS]
1051 << "filetools(getFormatFromContents)\n"
1052 << "\tFile type not recognised before EOF!"
1058 string const stamp = str.substr(0,2);
1059 if (firstLine && str.size() >= 2) {
1060 // at first we check for a zipped file, because this
1061 // information is saved in the first bytes of the file!
1062 // also some graphic formats which save the information
1063 // in the first line, too.
1064 if (prefixIs(str, gzipStamp)) {
1067 } else if (stamp == zipStamp) {
1070 } else if (stamp == compressStamp) {
1071 format = "compress";
1073 // the graphics part
1074 } else if (stamp == "BM") {
1077 } else if (stamp == "\001\332") {
1081 // Don't need to use str.at(0), str.at(1) because
1082 // we already know that str.size() >= 2
1083 } else if (str[0] == 'P') {
1099 } else if ((stamp == "II") || (stamp == "MM")) {
1102 } else if (prefixIs(str,"%TGIF")) {
1105 } else if (prefixIs(str,"#FIG")) {
1108 } else if (prefixIs(str,"GIF")) {
1111 } else if (str.size() > 3) {
1112 int const c = ((str[0] << 24) & (str[1] << 16) &
1113 (str[2] << 8) & str[3]);
1122 if (!format.empty())
1124 else if (contains(str,"EPSF"))
1125 // dummy, if we have wrong file description like
1126 // %!PS-Adobe-2.0EPSF"
1129 else if (contains(str,"Grace"))
1132 else if (contains(str,"JFIF"))
1135 else if (contains(str,"%PDF"))
1138 else if (contains(str,"PNG"))
1141 else if (contains(str,"%!PS-Adobe")) {
1144 if (contains(str,"EPSF"))
1150 else if (contains(str,"_bits[]"))
1153 else if (contains(str,"XPM") || contains(str, "static char *"))
1156 else if (contains(str,"BITPIX"))
1160 if (!format.empty()) {
1161 lyxerr[Debug::GRAPHICS]
1162 << "Recognised Fileformat: " << format << endl;
1166 lyxerr[Debug::GRAPHICS]
1167 << "filetools(getFormatFromContents)\n"
1168 << "\tCouldn't find a known format!\n";
1173 /// check for zipped file
1174 bool zippedFile(string const & name)
1176 string const type = getFormatFromContents(name);
1177 if (contains("gzip zip compress", type) && !type.empty())
1183 string const unzippedFileName(string const & zipped_file)
1185 string const ext = GetExtension(zipped_file);
1186 if (ext == "gz" || ext == "z" || ext == "Z")
1187 return ChangeExtension(zipped_file, string());
1188 return "unzipped_" + zipped_file;
1192 string const unzipFile(string const & zipped_file, string const & unzipped_file)
1194 string const tempfile = unzipped_file.empty() ?
1195 unzippedFileName(zipped_file) : unzipped_file;
1197 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1199 one.startscript(Systemcall::Wait, command);
1200 // test that command was executed successfully (anon)
1201 // yes, please do. (Lgb)
1206 string const MakeDisplayPath(string const & path, unsigned int threshold)
1210 string const home(package().home_dir());
1212 // replace /home/blah with ~/
1213 if (prefixIs(str, home))
1214 str = subst(str, home, "~");
1216 if (str.length() <= threshold)
1219 string const prefix = ".../";
1222 while (str.length() > threshold)
1223 str = split(str, temp, '/');
1225 // Did we shorten everything away?
1227 // Yes, filename itself is too long.
1228 // Pick the start and the end of the filename.
1229 str = OnlyFilename(path);
1230 string const head = str.substr(0, threshold / 2 - 3);
1232 string::size_type len = str.length();
1234 str.substr(len - threshold / 2 - 2, len - 1);
1235 str = head + "..." + tail;
1238 return prefix + str;
1242 bool LyXReadLink(string const & file, string & link, bool resolve)
1244 #ifdef HAVE_READLINK
1245 char linkbuffer[512];
1246 // Should be PATH_MAX but that needs autconf support
1247 int const nRead = ::readlink(file.c_str(),
1248 linkbuffer, sizeof(linkbuffer) - 1);
1251 linkbuffer[nRead] = '\0'; // terminator
1253 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1263 cmd_ret const RunCommand(string const & cmd)
1265 // FIXME: replace all calls to RunCommand with ForkedCall
1266 // (if the output is not needed) or the code in ispell.C
1267 // (if the output is needed).
1269 // One question is if we should use popen or
1270 // create our own popen based on fork, exec, pipe
1271 // of course the best would be to have a
1272 // pstream (process stream), with the
1273 // variants ipstream, opstream
1275 sigset_t newMask, oldMask;
1276 sigemptyset(&oldMask);
1277 sigemptyset(&newMask);
1278 sigaddset(&newMask, SIGCHLD);
1280 // Block the SIGCHLD signal.
1281 sigprocmask(SIG_BLOCK, &newMask, &oldMask);
1283 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1285 // (Claus Hentschel) Check if popen was succesful ;-)
1287 return make_pair(-1, string());
1288 lyxerr << "RunCommand:: could not start child process" << endl;
1294 ret += static_cast<char>(c);
1297 int const pret = pclose(inf);
1299 perror("RunCommand:: could not terminate child process");
1301 // Unblock the SIGCHLD signal and restore the old mask.
1302 sigprocmask(SIG_SETMASK, &oldMask, 0);
1304 return make_pair(pret, ret);
1308 string const findtexfile(string const & fil, string const & /*format*/)
1310 /* There is no problem to extend this function too use other
1311 methods to look for files. It could be setup to look
1312 in environment paths and also if wanted as a last resort
1313 to a recursive find. One of the easier extensions would
1314 perhaps be to use the LyX file lookup methods. But! I am
1315 going to implement this until I see some demand for it.
1319 // If the file can be found directly, we just return a
1320 // absolute path version of it.
1321 if (FileInfo(fil).exist())
1322 return MakeAbsPath(fil);
1324 // No we try to find it using kpsewhich.
1325 // It seems from the kpsewhich manual page that it is safe to use
1326 // kpsewhich without --format: "When the --format option is not
1327 // given, the search path used when looking for a file is inferred
1328 // from the name given, by looking for a known extension. If no
1329 // known extension is found, the search path for TeX source files
1331 // However, we want to take advantage of the format sine almost all
1332 // the different formats has environment variables that can be used
1333 // to controll which paths to search. f.ex. bib looks in
1334 // BIBINPUTS and TEXBIB. Small list follows:
1335 // bib - BIBINPUTS, TEXBIB
1337 // graphic/figure - TEXPICTS, TEXINPUTS
1338 // ist - TEXINDEXSTYLE, INDEXSTYLE
1339 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1341 // tfm - TFMFONTS, TEXFONTS
1342 // This means that to use kpsewhich in the best possible way we
1343 // should help it by setting additional path in the approp. envir.var.
1344 string const kpsecmd = "kpsewhich " + fil;
1346 cmd_ret const c = RunCommand(kpsecmd);
1348 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1349 << "kpse result = `" << rtrim(c.second, "\n")
1352 return os::internal_path(rtrim(c.second, "\n\r"));
1358 void removeAutosaveFile(string const & filename)
1360 string a = OnlyPath(filename);
1362 a += OnlyFilename(filename);
1364 FileInfo const fileinfo(a);
1365 if (fileinfo.exist())
1370 void readBB_lyxerrMessage(string const & file, bool & zipped,
1371 string const & message)
1373 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1374 << message << std::endl;
1375 #ifdef WITH_WARNINGS
1376 #warning Why is this func deleting a file? (Lgb)
1383 string const readBB_from_PSFile(string const & file)
1385 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1386 // It seems that every command in the header has an own line,
1387 // getline() should work for all files.
1388 // On the other hand some plot programs write the bb at the
1389 // end of the file. Than we have in the header:
1390 // %%BoundingBox: (atend)
1391 // In this case we must check the end.
1392 bool zipped = zippedFile(file);
1393 string const file_ = zipped ?
1394 string(unzipFile(file)) : string(file);
1395 string const format = getFormatFromContents(file_);
1397 if (format != "eps" && format != "ps") {
1398 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1402 std::ifstream is(file_.c_str());
1406 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1407 string const bb = ltrim(s.substr(14));
1408 readBB_lyxerrMessage(file_, zipped, bb);
1412 readBB_lyxerrMessage(file_, zipped, "no bb found");
1417 int compare_timestamps(string const & file1, string const & file2)
1419 BOOST_ASSERT(AbsolutePath(file1) && AbsolutePath(file2));
1421 // If the original is newer than the copy, then copy the original
1422 // to the new directory.
1427 if (f1.exist() && f2.exist()) {
1428 double const tmp = difftime(f1.getModificationTime(),
1429 f2.getModificationTime());
1431 cmp = tmp > 0 ? 1 : -1;
1433 } else if (f1.exist()) {
1435 } else if (f2.exist()) {
1442 } //namespace support