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)) {
584 if (IsDirWriteable(deflt))
585 // deflt could not be created because it
586 // did exist already, so let's create our own
589 Path p(user_lyxdir());
591 return createTmpDir(deflt, "lyx_tmpdir");
593 // some other error occured.
595 Path p(user_lyxdir());
597 return createTmpDir("/tmp", "lyx_tmpdir");
602 Path p(user_lyxdir());
604 return createTmpDir("/tmp", "lyx_tmpdir");
609 bool createDirectory(string const & path, int permission)
611 string temp(rtrim(os::internal_path(path), "/"));
613 BOOST_ASSERT(!temp.empty());
615 if (mkdir(temp, permission))
622 // Strip filename from path name
623 string const OnlyPath(string const & Filename)
625 // If empty filename, return empty
626 if (Filename.empty()) return Filename;
628 // Find last / or start of filename
629 string::size_type j = Filename.rfind('/');
630 if (j == string::npos)
632 return Filename.substr(0, j + 1);
636 // Convert relative path into absolute path based on a basepath.
637 // If relpath is absolute, just use that.
638 // If basepath is empty, use CWD as base.
639 string const MakeAbsPath(string const & RelPath, string const & BasePath)
641 // checks for already absolute path
642 if (os::is_absolute_path(RelPath))
645 // Copies given paths
646 string TempRel(os::internal_path(RelPath));
647 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
648 TempRel = subst(TempRel, "//", "/");
652 if (os::is_absolute_path(BasePath))
655 TempBase = AddPath(getcwd(), BasePath);
657 // Handle /./ at the end of the path
658 while (suffixIs(TempBase, "/./"))
659 TempBase.erase(TempBase.length() - 2);
661 // processes relative path
662 string RTemp(TempRel);
665 while (!RTemp.empty()) {
667 RTemp = split(RTemp, Temp, '/');
669 if (Temp == ".") continue;
671 // Remove one level of TempBase
672 string::difference_type i = TempBase.length() - 2;
675 while (i > 0 && TempBase[i] != '/') --i;
679 while (i > 2 && TempBase[i] != '/') --i;
682 TempBase.erase(i, string::npos);
685 } else if (Temp.empty() && !RTemp.empty()) {
686 TempBase = os::current_root() + RTemp;
689 // Add this piece to TempBase
690 if (!suffixIs(TempBase, '/'))
696 // returns absolute path
697 return os::internal_path(TempBase);
701 // Correctly append filename to the pathname.
702 // If pathname is '.', then don't use pathname.
703 // Chops any path of filename.
704 string const AddName(string const & path, string const & fname)
707 string const basename(OnlyFilename(fname));
711 if (path != "." && path != "./" && !path.empty()) {
712 buf = os::internal_path(path);
713 if (!suffixIs(path, '/'))
717 return buf + basename;
721 // Strips path from filename
722 string const OnlyFilename(string const & fname)
727 string::size_type j = fname.rfind('/');
728 if (j == string::npos) // no '/' in fname
732 return fname.substr(j + 1);
736 /// Returns true is path is absolute
737 bool AbsolutePath(string const & path)
739 return os::is_absolute_path(path);
744 // Create absolute path. If impossible, don't do anything
745 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
746 string const ExpandPath(string const & path)
748 // checks for already absolute path
749 string RTemp(ReplaceEnvironmentPath(path));
750 if (os::is_absolute_path(RTemp))
754 string const copy(RTemp);
757 RTemp = split(RTemp, Temp, '/');
760 return getcwd() + '/' + RTemp;
763 return package().home_dir() + '/' + RTemp;
766 return MakeAbsPath(copy);
768 // Don't know how to handle this
774 // Constracts path/../path
775 // Can't handle "../../" or "/../" (Asger)
776 // Also converts paths like /foo//bar ==> /foo/bar
777 string const NormalizePath(string const & path)
783 if (os::is_absolute_path(path))
786 // Make implicit current directory explicit
789 // Normalise paths like /foo//bar ==> /foo/bar
790 boost::RegEx regex("/{2,}");
791 RTemp = regex.Merge(RTemp, "/");
793 while (!RTemp.empty()) {
795 RTemp = split(RTemp, Temp, '/');
799 } else if (Temp == "..") {
800 // Remove one level of TempBase
801 string::difference_type i = TempBase.length() - 2;
802 while (i > 0 && TempBase[i] != '/')
804 if (i >= 0 && TempBase[i] == '/')
805 TempBase.erase(i + 1, string::npos);
809 TempBase += Temp + '/';
813 // returns absolute path
818 string const GetFileContents(string const & fname)
820 FileInfo finfo(fname);
822 ifstream ifs(fname.c_str());
830 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
835 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
836 string const ReplaceEnvironmentPath(string const & path)
838 // ${VAR} is defined as
839 // $\{[A-Za-z_][A-Za-z_0-9]*\}
840 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
842 // $VAR is defined as:
843 // $\{[A-Za-z_][A-Za-z_0-9]*\}
844 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
846 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
847 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
850 string result = path;
852 regex_match(result, what, envvar_br_re);
853 if (!what[0].matched) {
854 regex_match(result, what, envvar_re);
855 if (!what[0].matched)
858 result = what.str(1) + GetEnv(what.str(2)) + what.str(3);
864 // Make relative path out of two absolute paths
865 string const MakeRelPath(string const & abspath, string const & basepath)
866 // Makes relative path out of absolute path. If it is deeper than basepath,
867 // it's easy. If basepath and abspath share something (they are all deeper
868 // than some directory), it'll be rendered using ..'s. If they are completely
869 // different, then the absolute path will be used as relative path.
871 string::size_type const abslen = abspath.length();
872 string::size_type const baselen = basepath.length();
874 string::size_type i = os::common_path(abspath, basepath);
877 // actually no match - cannot make it relative
881 // Count how many dirs there are in basepath above match
882 // and append as many '..''s into relpath
884 string::size_type j = i;
885 while (j < baselen) {
886 if (basepath[j] == '/') {
887 if (j + 1 == baselen)
894 // Append relative stuff from common directory to abspath
895 if (abspath[i] == '/')
897 for (; i < abslen; ++i)
900 if (suffixIs(buf, '/'))
901 buf.erase(buf.length() - 1);
902 // Substitute empty with .
909 // Append sub-directory(ies) to a path in an intelligent way
910 string const AddPath(string const & path, string const & path_2)
913 string const path2 = os::internal_path(path_2);
915 if (!path.empty() && path != "." && path != "./") {
916 buf = os::internal_path(path);
917 if (path[path.length() - 1] != '/')
921 if (!path2.empty()) {
922 string::size_type const p2start = path2.find_first_not_of('/');
923 string::size_type const p2end = path2.find_last_not_of('/');
924 string const tmp = path2.substr(p2start, p2end - p2start + 1);
932 Change extension of oldname to extension.
933 Strips path off if no_path == true.
934 If no extension on oldname, just appends.
936 string const ChangeExtension(string const & oldname, string const & extension)
938 string::size_type const last_slash = oldname.rfind('/');
939 string::size_type last_dot = oldname.rfind('.');
940 if (last_dot < last_slash && last_slash != string::npos)
941 last_dot = string::npos;
944 // Make sure the extension starts with a dot
945 if (!extension.empty() && extension[0] != '.')
946 ext= '.' + extension;
950 return os::internal_path(oldname.substr(0, last_dot) + ext);
954 /// Return the extension of the file (not including the .)
955 string const GetExtension(string const & name)
957 string::size_type const last_slash = name.rfind('/');
958 string::size_type const last_dot = name.rfind('.');
959 if (last_dot != string::npos &&
960 (last_slash == string::npos || last_dot > last_slash))
961 return name.substr(last_dot + 1,
962 name.length() - (last_dot + 1));
968 // the different filetypes and what they contain in one of the first lines
969 // (dots are any characters). (Herbert 20020131)
972 // EPS %!PS-Adobe-3.0 EPSF...
979 // PBM P1... or P4 (B/W)
980 // PGM P2... or P5 (Grayscale)
981 // PPM P3... or P6 (color)
982 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
983 // SGI \001\332... (decimal 474)
985 // TIFF II... or MM...
987 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
988 // ...static char *...
989 // XWD \000\000\000\151 (0x00006900) decimal 105
991 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
992 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
993 // Z \037\235 UNIX compress
995 string const getFormatFromContents(string const & filename)
998 if (filename.empty() || !IsFileReadable(filename))
1001 ifstream ifs(filename.c_str());
1003 // Couldn't open file...
1007 string const gzipStamp = "\037\213";
1010 string const zipStamp = "PK";
1013 string const compressStamp = "\037\235";
1015 // Maximum strings to read
1016 int const max_count = 50;
1021 bool firstLine = true;
1022 while ((count++ < max_count) && format.empty()) {
1024 lyxerr[Debug::GRAPHICS]
1025 << "filetools(getFormatFromContents)\n"
1026 << "\tFile type not recognised before EOF!"
1032 string const stamp = str.substr(0,2);
1033 if (firstLine && str.size() >= 2) {
1034 // at first we check for a zipped file, because this
1035 // information is saved in the first bytes of the file!
1036 // also some graphic formats which save the information
1037 // in the first line, too.
1038 if (prefixIs(str, gzipStamp)) {
1041 } else if (stamp == zipStamp) {
1044 } else if (stamp == compressStamp) {
1045 format = "compress";
1047 // the graphics part
1048 } else if (stamp == "BM") {
1051 } else if (stamp == "\001\332") {
1055 // Don't need to use str.at(0), str.at(1) because
1056 // we already know that str.size() >= 2
1057 } else if (str[0] == 'P') {
1073 } else if ((stamp == "II") || (stamp == "MM")) {
1076 } else if (prefixIs(str,"%TGIF")) {
1079 } else if (prefixIs(str,"#FIG")) {
1082 } else if (prefixIs(str,"GIF")) {
1085 } else if (str.size() > 3) {
1086 int const c = ((str[0] << 24) & (str[1] << 16) &
1087 (str[2] << 8) & str[3]);
1096 if (!format.empty())
1098 else if (contains(str,"EPSF"))
1099 // dummy, if we have wrong file description like
1100 // %!PS-Adobe-2.0EPSF"
1103 else if (contains(str,"Grace"))
1106 else if (contains(str,"JFIF"))
1109 else if (contains(str,"%PDF"))
1112 else if (contains(str,"PNG"))
1115 else if (contains(str,"%!PS-Adobe")) {
1118 if (contains(str,"EPSF"))
1124 else if (contains(str,"_bits[]"))
1127 else if (contains(str,"XPM") || contains(str, "static char *"))
1130 else if (contains(str,"BITPIX"))
1134 if (!format.empty()) {
1135 lyxerr[Debug::GRAPHICS]
1136 << "Recognised Fileformat: " << format << endl;
1140 lyxerr[Debug::GRAPHICS]
1141 << "filetools(getFormatFromContents)\n"
1142 << "\tCouldn't find a known format!\n";
1147 /// check for zipped file
1148 bool zippedFile(string const & name)
1150 string const type = getFormatFromContents(name);
1151 if (contains("gzip zip compress", type) && !type.empty())
1157 string const unzippedFileName(string const & zipped_file)
1159 string const ext = GetExtension(zipped_file);
1160 if (ext == "gz" || ext == "z" || ext == "Z")
1161 return ChangeExtension(zipped_file, string());
1162 return "unzipped_" + zipped_file;
1166 string const unzipFile(string const & zipped_file, string const & unzipped_file)
1168 string const tempfile = unzipped_file.empty() ?
1169 unzippedFileName(zipped_file) : unzipped_file;
1171 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1173 one.startscript(Systemcall::Wait, command);
1174 // test that command was executed successfully (anon)
1175 // yes, please do. (Lgb)
1180 string const MakeDisplayPath(string const & path, unsigned int threshold)
1184 string const home(package().home_dir());
1186 // replace /home/blah with ~/
1187 if (prefixIs(str, home))
1188 str = subst(str, home, "~");
1190 if (str.length() <= threshold)
1193 string const prefix = ".../";
1196 while (str.length() > threshold)
1197 str = split(str, temp, '/');
1199 // Did we shorten everything away?
1201 // Yes, filename itself is too long.
1202 // Pick the start and the end of the filename.
1203 str = OnlyFilename(path);
1204 string const head = str.substr(0, threshold / 2 - 3);
1206 string::size_type len = str.length();
1208 str.substr(len - threshold / 2 - 2, len - 1);
1209 str = head + "..." + tail;
1212 return prefix + str;
1216 bool LyXReadLink(string const & file, string & link, bool resolve)
1218 #ifdef HAVE_READLINK
1219 char linkbuffer[512];
1220 // Should be PATH_MAX but that needs autconf support
1221 int const nRead = ::readlink(file.c_str(),
1222 linkbuffer, sizeof(linkbuffer) - 1);
1225 linkbuffer[nRead] = '\0'; // terminator
1227 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1237 cmd_ret const RunCommand(string const & cmd)
1239 // FIXME: replace all calls to RunCommand with ForkedCall
1240 // (if the output is not needed) or the code in ispell.C
1241 // (if the output is needed).
1243 // One question is if we should use popen or
1244 // create our own popen based on fork, exec, pipe
1245 // of course the best would be to have a
1246 // pstream (process stream), with the
1247 // variants ipstream, opstream
1249 sigset_t newMask, oldMask;
1250 sigemptyset(&oldMask);
1251 sigemptyset(&newMask);
1252 sigaddset(&newMask, SIGCHLD);
1254 // Block the SIGCHLD signal.
1255 sigprocmask(SIG_BLOCK, &newMask, &oldMask);
1257 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1259 // (Claus Hentschel) Check if popen was succesful ;-)
1261 return make_pair(-1, string());
1262 lyxerr << "RunCommand:: could not start child process" << endl;
1268 ret += static_cast<char>(c);
1271 int const pret = pclose(inf);
1273 perror("RunCommand:: could not terminate child process");
1275 // Unblock the SIGCHLD signal and restore the old mask.
1276 sigprocmask(SIG_SETMASK, &oldMask, 0);
1278 return make_pair(pret, ret);
1282 string const findtexfile(string const & fil, string const & /*format*/)
1284 /* There is no problem to extend this function too use other
1285 methods to look for files. It could be setup to look
1286 in environment paths and also if wanted as a last resort
1287 to a recursive find. One of the easier extensions would
1288 perhaps be to use the LyX file lookup methods. But! I am
1289 going to implement this until I see some demand for it.
1293 // If the file can be found directly, we just return a
1294 // absolute path version of it.
1295 if (FileInfo(fil).exist())
1296 return MakeAbsPath(fil);
1298 // No we try to find it using kpsewhich.
1299 // It seems from the kpsewhich manual page that it is safe to use
1300 // kpsewhich without --format: "When the --format option is not
1301 // given, the search path used when looking for a file is inferred
1302 // from the name given, by looking for a known extension. If no
1303 // known extension is found, the search path for TeX source files
1305 // However, we want to take advantage of the format sine almost all
1306 // the different formats has environment variables that can be used
1307 // to controll which paths to search. f.ex. bib looks in
1308 // BIBINPUTS and TEXBIB. Small list follows:
1309 // bib - BIBINPUTS, TEXBIB
1311 // graphic/figure - TEXPICTS, TEXINPUTS
1312 // ist - TEXINDEXSTYLE, INDEXSTYLE
1313 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1315 // tfm - TFMFONTS, TEXFONTS
1316 // This means that to use kpsewhich in the best possible way we
1317 // should help it by setting additional path in the approp. envir.var.
1318 string const kpsecmd = "kpsewhich " + fil;
1320 cmd_ret const c = RunCommand(kpsecmd);
1322 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1323 << "kpse result = `" << rtrim(c.second, "\n")
1326 return os::internal_path(rtrim(c.second, "\n\r"));
1332 void removeAutosaveFile(string const & filename)
1334 string a = OnlyPath(filename);
1336 a += OnlyFilename(filename);
1338 FileInfo const fileinfo(a);
1339 if (fileinfo.exist())
1344 void readBB_lyxerrMessage(string const & file, bool & zipped,
1345 string const & message)
1347 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1348 << message << std::endl;
1349 #ifdef WITH_WARNINGS
1350 #warning Why is this func deleting a file? (Lgb)
1357 string const readBB_from_PSFile(string const & file)
1359 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1360 // It seems that every command in the header has an own line,
1361 // getline() should work for all files.
1362 // On the other hand some plot programs write the bb at the
1363 // end of the file. Than we have in the header:
1364 // %%BoundingBox: (atend)
1365 // In this case we must check the end.
1366 bool zipped = zippedFile(file);
1367 string const file_ = zipped ?
1368 string(unzipFile(file)) : string(file);
1369 string const format = getFormatFromContents(file_);
1371 if (format != "eps" && format != "ps") {
1372 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1376 std::ifstream is(file_.c_str());
1380 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1381 string const bb = ltrim(s.substr(14));
1382 readBB_lyxerrMessage(file_, zipped, bb);
1386 readBB_lyxerrMessage(file_, zipped, "no bb found");
1391 int compare_timestamps(string const & file1, string const & file2)
1393 BOOST_ASSERT(AbsolutePath(file1) && AbsolutePath(file2));
1395 // If the original is newer than the copy, then copy the original
1396 // to the new directory.
1401 if (f1.exist() && f2.exist()) {
1402 double const tmp = difftime(f1.getModificationTime(),
1403 f2.getModificationTime());
1405 cmp = tmp > 0 ? 1 : -1;
1407 } else if (f1.exist()) {
1409 } else if (f2.exist()) {
1416 } //namespace support