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 // Assumes that the script name does not contain spaces.
367 string::size_type const start_script = pos1 + 4;
368 string::size_type const pos2 = command.find(' ', start_script);
369 string::size_type const size_script = pos2 == string::npos?
370 (command.size() - start_script) : pos2 - start_script;
372 // Does this script file exist?
373 string const script =
374 LibFileSearch(".", command.substr(start_script, size_script));
376 if (script.empty()) {
377 // Replace "$$s/" with ""
378 command.erase(pos1, 4);
380 // Replace "$$s/foo/some_script" with "<path to>/some_script".
381 string::size_type const size_replace = size_script + 4;
382 command.replace(pos1, size_replace, QuoteName(script));
389 string const GetEnv(string const & envname)
391 // f.ex. what about error checking?
392 char const * const ch = getenv(envname.c_str());
393 string const envstr = !ch ? "" : ch;
398 vector<string> const getEnvPath(string const & name)
400 typedef boost::char_separator<char> Separator;
401 typedef boost::tokenizer<Separator> Tokenizer;
403 string const env_var = GetEnv(name);
404 Separator const separator(string(1, os::path_separator()).c_str());
405 Tokenizer const tokens(env_var, separator);
406 Tokenizer::const_iterator it = tokens.begin();
407 Tokenizer::const_iterator const end = tokens.end();
409 std::vector<string> vars;
410 for (; it != end; ++it)
411 vars.push_back(os::internal_path(*it));
417 void setEnvPath(string const & name, vector<string> const & env)
419 char const separator(os::path_separator());
420 std::ostringstream ss;
421 vector<string>::const_iterator it = env.begin();
422 vector<string>::const_iterator const end = env.end();
423 for (; it != end; ++it) {
426 ss << os::external_path(*it);
428 putEnv(name + "=" + ss.str());
432 void prependEnvPath(string const & name, string const & prefix)
434 vector<string> env_var = getEnvPath(name);
436 typedef boost::char_separator<char> Separator;
437 typedef boost::tokenizer<Separator> Tokenizer;
439 Separator const separator(string(1, os::path_separator()).c_str());
441 // Prepend each new element to the list, removing identical elements
442 // that occur later in the list.
443 Tokenizer const tokens(prefix, separator);
444 vector<string> reversed_tokens(tokens.begin(), tokens.end());
446 typedef vector<string>::const_reverse_iterator token_iterator;
447 token_iterator it = reversed_tokens.rbegin();
448 token_iterator const end = reversed_tokens.rend();
449 for (; it != end; ++it) {
450 vector<string>::iterator remove_it =
451 std::remove(env_var.begin(), env_var.end(), *it);
452 env_var.erase(remove_it, env_var.end());
453 env_var.insert(env_var.begin(), *it);
456 setEnvPath(name, env_var);
460 bool putEnv(string const & envstr)
462 // CHECK Look at and fix this.
463 // f.ex. what about error checking?
465 #if defined (HAVE_SETENV)
467 string const value = split(envstr, name, '=');
468 int const retval = ::setenv(name.c_str(), value.c_str(), true);
469 #elif defined (HAVE_PUTENV)
470 // this leaks, but what can we do about it?
471 // Is doing a getenv() and a free() of the older value
472 // a good idea? (JMarc)
473 // Actually we don't have to leak...calling putenv like this
474 // should be enough: ... and this is obviously not enough if putenv
475 // does not make a copy of the string. It is also not very wise to
476 // put a string on the free store. If we have to leak we should do it
478 char * leaker = new char[envstr.length() + 1];
479 envstr.copy(leaker, envstr.length());
480 leaker[envstr.length()] = '\0';
481 int const retval = ::putenv(leaker);
483 // If putenv does not make a copy of the char const * this
484 // is very dangerous. OTOH if it does take a copy this is the
486 // The only implementation of putenv that I have seen does not
487 // allocate memory. _And_ after testing the putenv in glibc it
488 // seems that we need to make a copy of the string contents.
489 // I will enable the above.
490 //int retval = lyx::putenv(envstr.c_str());
492 // No environment setting function. Can this happen?
493 int const retval = 1; //return an error condition.
501 int DeleteAllFilesInDir(string const & path)
503 // I have decided that we will be using parts from the boost
504 // library. Check out http://www.boost.org/
505 // For directory access we will then use the directory_iterator.
506 // Then the code will be something like:
507 // directory_iterator dit(path);
508 // directory_iterator dend;
509 // if (dit == dend) {
512 // for (; dit != dend; ++dit) {
513 // string filename(*dit);
514 // if (filename == "." || filename == "..")
516 // string unlinkpath(AddName(path, filename));
517 // lyx::unlink(unlinkpath);
520 DIR * dir = ::opendir(path.c_str());
525 int return_value = 0;
526 while ((de = readdir(dir))) {
527 string const temp = de->d_name;
528 if (temp == "." || temp == "..")
530 string const unlinkpath = AddName (path, temp);
532 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
536 FileInfo fi(unlinkpath);
537 if (fi.isOK() && fi.isDir()) {
538 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
539 deleted &= (rmdir(unlinkpath) == 0);
541 deleted &= (unlink(unlinkpath) == 0);
550 string const createTmpDir(string const & tempdir, string const & mask)
553 << "createTmpDir: tempdir=`" << tempdir << "'\n"
554 << "createTmpDir: mask=`" << mask << '\'' << endl;
556 string const tmpfl(tempName(tempdir, mask));
557 // lyx::tempName actually creates a file to make sure that it
558 // stays unique. So we have to delete it before we can create
559 // a dir with the same name. Note also that we are not thread
560 // safe because of the gap between unlink and mkdir. (Lgb)
563 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
564 lyxerr << "LyX could not create the temporary directory '"
565 << tmpfl << "'" << endl;
569 return MakeAbsPath(tmpfl);
575 int destroyDir(string const & tmpdir)
578 Path p(user_lyxdir());
580 if (DeleteAllFilesInDir(tmpdir))
590 string const createBufferTmpDir()
593 // We are in our own directory. Why bother to mangle name?
594 // In fact I wrote this code to circumvent a problematic behaviour
595 // (bug?) of EMX mkstemp().
597 package().temp_dir() + "/lyx_tmpbuf" +
598 convert<string>(count++);
600 if (mkdir(tmpfl, 0777)) {
601 lyxerr << "LyX could not create the temporary directory '"
602 << tmpfl << "'" << endl;
609 string const createLyXTmpDir(string const & deflt)
611 if (!deflt.empty() && deflt != "/tmp") {
612 if (mkdir(deflt, 0777)) {
614 Path p(package().user_support());
616 if (IsDirWriteable(deflt)) {
617 // deflt could not be created because it
618 // did exist already, so let's create our own
620 return createTmpDir(deflt, "lyx_tmpdir");
622 // some other error occured.
623 return createTmpDir("/tmp", "lyx_tmpdir");
629 Path p(package().user_support());
631 return createTmpDir("/tmp", "lyx_tmpdir");
636 bool createDirectory(string const & path, int permission)
638 string temp(rtrim(os::internal_path(path), "/"));
640 BOOST_ASSERT(!temp.empty());
642 if (mkdir(temp, permission))
649 // Strip filename from path name
650 string const OnlyPath(string const & Filename)
652 // If empty filename, return empty
653 if (Filename.empty()) return Filename;
655 // Find last / or start of filename
656 string::size_type j = Filename.rfind('/');
657 if (j == string::npos)
659 return Filename.substr(0, j + 1);
663 // Convert relative path into absolute path based on a basepath.
664 // If relpath is absolute, just use that.
665 // If basepath is empty, use CWD as base.
666 string const MakeAbsPath(string const & RelPath, string const & BasePath)
668 // checks for already absolute path
669 if (os::is_absolute_path(RelPath))
672 // Copies given paths
673 string TempRel(os::internal_path(RelPath));
674 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
675 TempRel = subst(TempRel, "//", "/");
679 if (os::is_absolute_path(BasePath))
682 TempBase = AddPath(getcwd(), BasePath);
684 // Handle /./ at the end of the path
685 while (suffixIs(TempBase, "/./"))
686 TempBase.erase(TempBase.length() - 2);
688 // processes relative path
689 string RTemp(TempRel);
692 while (!RTemp.empty()) {
694 RTemp = split(RTemp, Temp, '/');
696 if (Temp == ".") continue;
698 // Remove one level of TempBase
699 string::difference_type i = TempBase.length() - 2;
702 while (i > 0 && TempBase[i] != '/') --i;
706 while (i > 2 && TempBase[i] != '/') --i;
709 TempBase.erase(i, string::npos);
712 } else if (Temp.empty() && !RTemp.empty()) {
713 TempBase = os::current_root() + RTemp;
716 // Add this piece to TempBase
717 if (!suffixIs(TempBase, '/'))
723 // returns absolute path
724 return os::internal_path(TempBase);
728 // Correctly append filename to the pathname.
729 // If pathname is '.', then don't use pathname.
730 // Chops any path of filename.
731 string const AddName(string const & path, string const & fname)
734 string const basename(OnlyFilename(fname));
738 if (path != "." && path != "./" && !path.empty()) {
739 buf = os::internal_path(path);
740 if (!suffixIs(path, '/'))
744 return buf + basename;
748 // Strips path from filename
749 string const OnlyFilename(string const & fname)
754 string::size_type j = fname.rfind('/');
755 if (j == string::npos) // no '/' in fname
759 return fname.substr(j + 1);
763 /// Returns true is path is absolute
764 bool AbsolutePath(string const & path)
766 return os::is_absolute_path(path);
771 // Create absolute path. If impossible, don't do anything
772 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
773 string const ExpandPath(string const & path)
775 // checks for already absolute path
776 string RTemp(ReplaceEnvironmentPath(path));
777 if (os::is_absolute_path(RTemp))
781 string const copy(RTemp);
784 RTemp = split(RTemp, Temp, '/');
787 return getcwd() + '/' + RTemp;
790 return package().home_dir() + '/' + RTemp;
793 return MakeAbsPath(copy);
795 // Don't know how to handle this
801 // Constracts path/../path
802 // Can't handle "../../" or "/../" (Asger)
803 // Also converts paths like /foo//bar ==> /foo/bar
804 string const NormalizePath(string const & path)
810 if (os::is_absolute_path(path))
813 // Make implicit current directory explicit
816 // Normalise paths like /foo//bar ==> /foo/bar
817 boost::RegEx regex("/{2,}");
818 RTemp = regex.Merge(RTemp, "/");
820 while (!RTemp.empty()) {
822 RTemp = split(RTemp, Temp, '/');
826 } else if (Temp == "..") {
827 // Remove one level of TempBase
828 string::difference_type i = TempBase.length() - 2;
829 while (i > 0 && TempBase[i] != '/')
831 if (i >= 0 && TempBase[i] == '/')
832 TempBase.erase(i + 1, string::npos);
836 TempBase += Temp + '/';
840 // returns absolute path
845 string const GetFileContents(string const & fname)
847 FileInfo finfo(fname);
849 ifstream ifs(fname.c_str());
857 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
862 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
863 string const ReplaceEnvironmentPath(string const & path)
865 // ${VAR} is defined as
866 // $\{[A-Za-z_][A-Za-z_0-9]*\}
867 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
869 // $VAR is defined as:
870 // $\{[A-Za-z_][A-Za-z_0-9]*\}
871 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
873 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
874 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
877 string result = path;
879 regex_match(result, what, envvar_br_re);
880 if (!what[0].matched) {
881 regex_match(result, what, envvar_re);
882 if (!what[0].matched)
885 result = what.str(1) + GetEnv(what.str(2)) + what.str(3);
891 // Make relative path out of two absolute paths
892 string const MakeRelPath(string const & abspath, string const & basepath)
893 // Makes relative path out of absolute path. If it is deeper than basepath,
894 // it's easy. If basepath and abspath share something (they are all deeper
895 // than some directory), it'll be rendered using ..'s. If they are completely
896 // different, then the absolute path will be used as relative path.
898 string::size_type const abslen = abspath.length();
899 string::size_type const baselen = basepath.length();
901 string::size_type i = os::common_path(abspath, basepath);
904 // actually no match - cannot make it relative
908 // Count how many dirs there are in basepath above match
909 // and append as many '..''s into relpath
911 string::size_type j = i;
912 while (j < baselen) {
913 if (basepath[j] == '/') {
914 if (j + 1 == baselen)
921 // Append relative stuff from common directory to abspath
922 if (abspath[i] == '/')
924 for (; i < abslen; ++i)
927 if (suffixIs(buf, '/'))
928 buf.erase(buf.length() - 1);
929 // Substitute empty with .
936 // Append sub-directory(ies) to a path in an intelligent way
937 string const AddPath(string const & path, string const & path_2)
940 string const path2 = os::internal_path(path_2);
942 if (!path.empty() && path != "." && path != "./") {
943 buf = os::internal_path(path);
944 if (path[path.length() - 1] != '/')
948 if (!path2.empty()) {
949 string::size_type const p2start = path2.find_first_not_of('/');
950 string::size_type const p2end = path2.find_last_not_of('/');
951 string const tmp = path2.substr(p2start, p2end - p2start + 1);
959 Change extension of oldname to extension.
960 Strips path off if no_path == true.
961 If no extension on oldname, just appends.
963 string const ChangeExtension(string const & oldname, string const & extension)
965 string::size_type const last_slash = oldname.rfind('/');
966 string::size_type last_dot = oldname.rfind('.');
967 if (last_dot < last_slash && last_slash != string::npos)
968 last_dot = string::npos;
971 // Make sure the extension starts with a dot
972 if (!extension.empty() && extension[0] != '.')
973 ext= '.' + extension;
977 return os::internal_path(oldname.substr(0, last_dot) + ext);
981 /// Return the extension of the file (not including the .)
982 string const GetExtension(string const & name)
984 string::size_type const last_slash = name.rfind('/');
985 string::size_type const last_dot = name.rfind('.');
986 if (last_dot != string::npos &&
987 (last_slash == string::npos || last_dot > last_slash))
988 return name.substr(last_dot + 1,
989 name.length() - (last_dot + 1));
995 // the different filetypes and what they contain in one of the first lines
996 // (dots are any characters). (Herbert 20020131)
999 // EPS %!PS-Adobe-3.0 EPSF...
1001 // FITS ...BITPIX...
1006 // PBM P1... or P4 (B/W)
1007 // PGM P2... or P5 (Grayscale)
1008 // PPM P3... or P6 (color)
1009 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
1010 // SGI \001\332... (decimal 474)
1012 // TIFF II... or MM...
1013 // XBM ..._bits[]...
1014 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
1015 // ...static char *...
1016 // XWD \000\000\000\151 (0x00006900) decimal 105
1018 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
1019 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
1020 // Z \037\235 UNIX compress
1022 string const getFormatFromContents(string const & filename)
1025 if (filename.empty() || !IsFileReadable(filename))
1028 ifstream ifs(filename.c_str());
1030 // Couldn't open file...
1034 string const gzipStamp = "\037\213";
1037 string const zipStamp = "PK";
1040 string const compressStamp = "\037\235";
1042 // Maximum strings to read
1043 int const max_count = 50;
1048 bool firstLine = true;
1049 while ((count++ < max_count) && format.empty()) {
1051 lyxerr[Debug::GRAPHICS]
1052 << "filetools(getFormatFromContents)\n"
1053 << "\tFile type not recognised before EOF!"
1059 string const stamp = str.substr(0,2);
1060 if (firstLine && str.size() >= 2) {
1061 // at first we check for a zipped file, because this
1062 // information is saved in the first bytes of the file!
1063 // also some graphic formats which save the information
1064 // in the first line, too.
1065 if (prefixIs(str, gzipStamp)) {
1068 } else if (stamp == zipStamp) {
1071 } else if (stamp == compressStamp) {
1072 format = "compress";
1074 // the graphics part
1075 } else if (stamp == "BM") {
1078 } else if (stamp == "\001\332") {
1082 // Don't need to use str.at(0), str.at(1) because
1083 // we already know that str.size() >= 2
1084 } else if (str[0] == 'P') {
1100 } else if ((stamp == "II") || (stamp == "MM")) {
1103 } else if (prefixIs(str,"%TGIF")) {
1106 } else if (prefixIs(str,"#FIG")) {
1109 } else if (prefixIs(str,"GIF")) {
1112 } else if (str.size() > 3) {
1113 int const c = ((str[0] << 24) & (str[1] << 16) &
1114 (str[2] << 8) & str[3]);
1123 if (!format.empty())
1125 else if (contains(str,"EPSF"))
1126 // dummy, if we have wrong file description like
1127 // %!PS-Adobe-2.0EPSF"
1130 else if (contains(str,"Grace"))
1133 else if (contains(str,"JFIF"))
1136 else if (contains(str,"%PDF"))
1139 else if (contains(str,"PNG"))
1142 else if (contains(str,"%!PS-Adobe")) {
1145 if (contains(str,"EPSF"))
1151 else if (contains(str,"_bits[]"))
1154 else if (contains(str,"XPM") || contains(str, "static char *"))
1157 else if (contains(str,"BITPIX"))
1161 if (!format.empty()) {
1162 lyxerr[Debug::GRAPHICS]
1163 << "Recognised Fileformat: " << format << endl;
1167 lyxerr[Debug::GRAPHICS]
1168 << "filetools(getFormatFromContents)\n"
1169 << "\tCouldn't find a known format!\n";
1174 /// check for zipped file
1175 bool zippedFile(string const & name)
1177 string const type = getFormatFromContents(name);
1178 if (contains("gzip zip compress", type) && !type.empty())
1184 string const unzippedFileName(string const & zipped_file)
1186 string const ext = GetExtension(zipped_file);
1187 if (ext == "gz" || ext == "z" || ext == "Z")
1188 return ChangeExtension(zipped_file, string());
1189 return "unzipped_" + zipped_file;
1193 string const unzipFile(string const & zipped_file, string const & unzipped_file)
1195 string const tempfile = unzipped_file.empty() ?
1196 unzippedFileName(zipped_file) : unzipped_file;
1198 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1200 one.startscript(Systemcall::Wait, command);
1201 // test that command was executed successfully (anon)
1202 // yes, please do. (Lgb)
1207 string const MakeDisplayPath(string const & path, unsigned int threshold)
1211 string const home(package().home_dir());
1213 // replace /home/blah with ~/
1214 if (prefixIs(str, home))
1215 str = subst(str, home, "~");
1217 if (str.length() <= threshold)
1220 string const prefix = ".../";
1223 while (str.length() > threshold)
1224 str = split(str, temp, '/');
1226 // Did we shorten everything away?
1228 // Yes, filename itself is too long.
1229 // Pick the start and the end of the filename.
1230 str = OnlyFilename(path);
1231 string const head = str.substr(0, threshold / 2 - 3);
1233 string::size_type len = str.length();
1235 str.substr(len - threshold / 2 - 2, len - 1);
1236 str = head + "..." + tail;
1239 return prefix + str;
1243 bool LyXReadLink(string const & file, string & link, bool resolve)
1245 #ifdef HAVE_READLINK
1246 char linkbuffer[512];
1247 // Should be PATH_MAX but that needs autconf support
1248 int const nRead = ::readlink(file.c_str(),
1249 linkbuffer, sizeof(linkbuffer) - 1);
1252 linkbuffer[nRead] = '\0'; // terminator
1254 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1264 cmd_ret const RunCommand(string const & cmd)
1266 // FIXME: replace all calls to RunCommand with ForkedCall
1267 // (if the output is not needed) or the code in ispell.C
1268 // (if the output is needed).
1270 // One question is if we should use popen or
1271 // create our own popen based on fork, exec, pipe
1272 // of course the best would be to have a
1273 // pstream (process stream), with the
1274 // variants ipstream, opstream
1276 sigset_t newMask, oldMask;
1277 sigemptyset(&oldMask);
1278 sigemptyset(&newMask);
1279 sigaddset(&newMask, SIGCHLD);
1281 // Block the SIGCHLD signal.
1282 sigprocmask(SIG_BLOCK, &newMask, &oldMask);
1284 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1286 // (Claus Hentschel) Check if popen was succesful ;-)
1288 return make_pair(-1, string());
1289 lyxerr << "RunCommand:: could not start child process" << endl;
1295 ret += static_cast<char>(c);
1298 int const pret = pclose(inf);
1300 perror("RunCommand:: could not terminate child process");
1302 // Unblock the SIGCHLD signal and restore the old mask.
1303 sigprocmask(SIG_SETMASK, &oldMask, 0);
1305 return make_pair(pret, ret);
1309 string const findtexfile(string const & fil, string const & /*format*/)
1311 /* There is no problem to extend this function too use other
1312 methods to look for files. It could be setup to look
1313 in environment paths and also if wanted as a last resort
1314 to a recursive find. One of the easier extensions would
1315 perhaps be to use the LyX file lookup methods. But! I am
1316 going to implement this until I see some demand for it.
1320 // If the file can be found directly, we just return a
1321 // absolute path version of it.
1322 if (FileInfo(fil).exist())
1323 return MakeAbsPath(fil);
1325 // No we try to find it using kpsewhich.
1326 // It seems from the kpsewhich manual page that it is safe to use
1327 // kpsewhich without --format: "When the --format option is not
1328 // given, the search path used when looking for a file is inferred
1329 // from the name given, by looking for a known extension. If no
1330 // known extension is found, the search path for TeX source files
1332 // However, we want to take advantage of the format sine almost all
1333 // the different formats has environment variables that can be used
1334 // to controll which paths to search. f.ex. bib looks in
1335 // BIBINPUTS and TEXBIB. Small list follows:
1336 // bib - BIBINPUTS, TEXBIB
1338 // graphic/figure - TEXPICTS, TEXINPUTS
1339 // ist - TEXINDEXSTYLE, INDEXSTYLE
1340 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1342 // tfm - TFMFONTS, TEXFONTS
1343 // This means that to use kpsewhich in the best possible way we
1344 // should help it by setting additional path in the approp. envir.var.
1345 string const kpsecmd = "kpsewhich " + fil;
1347 cmd_ret const c = RunCommand(kpsecmd);
1349 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1350 << "kpse result = `" << rtrim(c.second, "\n")
1353 return os::internal_path(rtrim(c.second, "\n\r"));
1359 void removeAutosaveFile(string const & filename)
1361 string a = OnlyPath(filename);
1363 a += OnlyFilename(filename);
1365 FileInfo const fileinfo(a);
1366 if (fileinfo.exist())
1371 void readBB_lyxerrMessage(string const & file, bool & zipped,
1372 string const & message)
1374 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1375 << message << std::endl;
1376 #ifdef WITH_WARNINGS
1377 #warning Why is this func deleting a file? (Lgb)
1384 string const readBB_from_PSFile(string const & file)
1386 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1387 // It seems that every command in the header has an own line,
1388 // getline() should work for all files.
1389 // On the other hand some plot programs write the bb at the
1390 // end of the file. Than we have in the header:
1391 // %%BoundingBox: (atend)
1392 // In this case we must check the end.
1393 bool zipped = zippedFile(file);
1394 string const file_ = zipped ?
1395 string(unzipFile(file)) : string(file);
1396 string const format = getFormatFromContents(file_);
1398 if (format != "eps" && format != "ps") {
1399 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1403 std::ifstream is(file_.c_str());
1407 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1408 string const bb = ltrim(s.substr(14));
1409 readBB_lyxerrMessage(file_, zipped, bb);
1413 readBB_lyxerrMessage(file_, zipped, "no bb found");
1418 int compare_timestamps(string const & file1, string const & file2)
1420 BOOST_ASSERT(AbsolutePath(file1) && AbsolutePath(file2));
1422 // If the original is newer than the copy, then copy the original
1423 // to the new directory.
1428 if (f1.exist() && f2.exist()) {
1429 double const tmp = difftime(f1.getModificationTime(),
1430 f2.getModificationTime());
1432 cmp = tmp > 0 ? 1 : -1;
1434 } else if (f1.exist()) {
1436 } else if (f2.exist()) {
1443 } //namespace support