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");
603 Path p(user_lyxdir());
605 return createTmpDir("/tmp", "lyx_tmpdir");
610 bool createDirectory(string const & path, int permission)
612 string temp(rtrim(os::internal_path(path), "/"));
614 BOOST_ASSERT(!temp.empty());
616 if (mkdir(temp, permission))
623 // Strip filename from path name
624 string const OnlyPath(string const & Filename)
626 // If empty filename, return empty
627 if (Filename.empty()) return Filename;
629 // Find last / or start of filename
630 string::size_type j = Filename.rfind('/');
631 if (j == string::npos)
633 return Filename.substr(0, j + 1);
637 // Convert relative path into absolute path based on a basepath.
638 // If relpath is absolute, just use that.
639 // If basepath is empty, use CWD as base.
640 string const MakeAbsPath(string const & RelPath, string const & BasePath)
642 // checks for already absolute path
643 if (os::is_absolute_path(RelPath))
646 // Copies given paths
647 string TempRel(os::internal_path(RelPath));
648 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
649 TempRel = subst(TempRel, "//", "/");
653 if (os::is_absolute_path(BasePath))
656 TempBase = AddPath(getcwd(), BasePath);
658 // Handle /./ at the end of the path
659 while (suffixIs(TempBase, "/./"))
660 TempBase.erase(TempBase.length() - 2);
662 // processes relative path
663 string RTemp(TempRel);
666 while (!RTemp.empty()) {
668 RTemp = split(RTemp, Temp, '/');
670 if (Temp == ".") continue;
672 // Remove one level of TempBase
673 string::difference_type i = TempBase.length() - 2;
676 while (i > 0 && TempBase[i] != '/') --i;
680 while (i > 2 && TempBase[i] != '/') --i;
683 TempBase.erase(i, string::npos);
686 } else if (Temp.empty() && !RTemp.empty()) {
687 TempBase = os::current_root() + RTemp;
690 // Add this piece to TempBase
691 if (!suffixIs(TempBase, '/'))
697 // returns absolute path
698 return os::internal_path(TempBase);
702 // Correctly append filename to the pathname.
703 // If pathname is '.', then don't use pathname.
704 // Chops any path of filename.
705 string const AddName(string const & path, string const & fname)
708 string const basename(OnlyFilename(fname));
712 if (path != "." && path != "./" && !path.empty()) {
713 buf = os::internal_path(path);
714 if (!suffixIs(path, '/'))
718 return buf + basename;
722 // Strips path from filename
723 string const OnlyFilename(string const & fname)
728 string::size_type j = fname.rfind('/');
729 if (j == string::npos) // no '/' in fname
733 return fname.substr(j + 1);
737 /// Returns true is path is absolute
738 bool AbsolutePath(string const & path)
740 return os::is_absolute_path(path);
745 // Create absolute path. If impossible, don't do anything
746 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
747 string const ExpandPath(string const & path)
749 // checks for already absolute path
750 string RTemp(ReplaceEnvironmentPath(path));
751 if (os::is_absolute_path(RTemp))
755 string const copy(RTemp);
758 RTemp = split(RTemp, Temp, '/');
761 return getcwd() + '/' + RTemp;
764 return package().home_dir() + '/' + RTemp;
767 return MakeAbsPath(copy);
769 // Don't know how to handle this
775 // Constracts path/../path
776 // Can't handle "../../" or "/../" (Asger)
777 // Also converts paths like /foo//bar ==> /foo/bar
778 string const NormalizePath(string const & path)
784 if (os::is_absolute_path(path))
787 // Make implicit current directory explicit
790 // Normalise paths like /foo//bar ==> /foo/bar
791 boost::RegEx regex("/{2,}");
792 RTemp = regex.Merge(RTemp, "/");
794 while (!RTemp.empty()) {
796 RTemp = split(RTemp, Temp, '/');
800 } else if (Temp == "..") {
801 // Remove one level of TempBase
802 string::difference_type i = TempBase.length() - 2;
803 while (i > 0 && TempBase[i] != '/')
805 if (i >= 0 && TempBase[i] == '/')
806 TempBase.erase(i + 1, string::npos);
810 TempBase += Temp + '/';
814 // returns absolute path
819 string const GetFileContents(string const & fname)
821 FileInfo finfo(fname);
823 ifstream ifs(fname.c_str());
831 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
836 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
837 string const ReplaceEnvironmentPath(string const & path)
839 // ${VAR} is defined as
840 // $\{[A-Za-z_][A-Za-z_0-9]*\}
841 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
843 // $VAR is defined as:
844 // $\{[A-Za-z_][A-Za-z_0-9]*\}
845 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
847 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
848 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
851 string result = path;
853 regex_match(result, what, envvar_br_re);
854 if (!what[0].matched) {
855 regex_match(result, what, envvar_re);
856 if (!what[0].matched)
859 result = what.str(1) + GetEnv(what.str(2)) + what.str(3);
865 // Make relative path out of two absolute paths
866 string const MakeRelPath(string const & abspath, string const & basepath)
867 // Makes relative path out of absolute path. If it is deeper than basepath,
868 // it's easy. If basepath and abspath share something (they are all deeper
869 // than some directory), it'll be rendered using ..'s. If they are completely
870 // different, then the absolute path will be used as relative path.
872 string::size_type const abslen = abspath.length();
873 string::size_type const baselen = basepath.length();
875 string::size_type i = os::common_path(abspath, basepath);
878 // actually no match - cannot make it relative
882 // Count how many dirs there are in basepath above match
883 // and append as many '..''s into relpath
885 string::size_type j = i;
886 while (j < baselen) {
887 if (basepath[j] == '/') {
888 if (j + 1 == baselen)
895 // Append relative stuff from common directory to abspath
896 if (abspath[i] == '/')
898 for (; i < abslen; ++i)
901 if (suffixIs(buf, '/'))
902 buf.erase(buf.length() - 1);
903 // Substitute empty with .
910 // Append sub-directory(ies) to a path in an intelligent way
911 string const AddPath(string const & path, string const & path_2)
914 string const path2 = os::internal_path(path_2);
916 if (!path.empty() && path != "." && path != "./") {
917 buf = os::internal_path(path);
918 if (path[path.length() - 1] != '/')
922 if (!path2.empty()) {
923 string::size_type const p2start = path2.find_first_not_of('/');
924 string::size_type const p2end = path2.find_last_not_of('/');
925 string const tmp = path2.substr(p2start, p2end - p2start + 1);
933 Change extension of oldname to extension.
934 Strips path off if no_path == true.
935 If no extension on oldname, just appends.
937 string const ChangeExtension(string const & oldname, string const & extension)
939 string::size_type const last_slash = oldname.rfind('/');
940 string::size_type last_dot = oldname.rfind('.');
941 if (last_dot < last_slash && last_slash != string::npos)
942 last_dot = string::npos;
945 // Make sure the extension starts with a dot
946 if (!extension.empty() && extension[0] != '.')
947 ext= '.' + extension;
951 return os::internal_path(oldname.substr(0, last_dot) + ext);
955 /// Return the extension of the file (not including the .)
956 string const GetExtension(string const & name)
958 string::size_type const last_slash = name.rfind('/');
959 string::size_type const last_dot = name.rfind('.');
960 if (last_dot != string::npos &&
961 (last_slash == string::npos || last_dot > last_slash))
962 return name.substr(last_dot + 1,
963 name.length() - (last_dot + 1));
969 // the different filetypes and what they contain in one of the first lines
970 // (dots are any characters). (Herbert 20020131)
973 // EPS %!PS-Adobe-3.0 EPSF...
980 // PBM P1... or P4 (B/W)
981 // PGM P2... or P5 (Grayscale)
982 // PPM P3... or P6 (color)
983 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
984 // SGI \001\332... (decimal 474)
986 // TIFF II... or MM...
988 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
989 // ...static char *...
990 // XWD \000\000\000\151 (0x00006900) decimal 105
992 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
993 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
994 // Z \037\235 UNIX compress
996 string const getFormatFromContents(string const & filename)
999 if (filename.empty() || !IsFileReadable(filename))
1002 ifstream ifs(filename.c_str());
1004 // Couldn't open file...
1008 string const gzipStamp = "\037\213";
1011 string const zipStamp = "PK";
1014 string const compressStamp = "\037\235";
1016 // Maximum strings to read
1017 int const max_count = 50;
1022 bool firstLine = true;
1023 while ((count++ < max_count) && format.empty()) {
1025 lyxerr[Debug::GRAPHICS]
1026 << "filetools(getFormatFromContents)\n"
1027 << "\tFile type not recognised before EOF!"
1033 string const stamp = str.substr(0,2);
1034 if (firstLine && str.size() >= 2) {
1035 // at first we check for a zipped file, because this
1036 // information is saved in the first bytes of the file!
1037 // also some graphic formats which save the information
1038 // in the first line, too.
1039 if (prefixIs(str, gzipStamp)) {
1042 } else if (stamp == zipStamp) {
1045 } else if (stamp == compressStamp) {
1046 format = "compress";
1048 // the graphics part
1049 } else if (stamp == "BM") {
1052 } else if (stamp == "\001\332") {
1056 // Don't need to use str.at(0), str.at(1) because
1057 // we already know that str.size() >= 2
1058 } else if (str[0] == 'P') {
1074 } else if ((stamp == "II") || (stamp == "MM")) {
1077 } else if (prefixIs(str,"%TGIF")) {
1080 } else if (prefixIs(str,"#FIG")) {
1083 } else if (prefixIs(str,"GIF")) {
1086 } else if (str.size() > 3) {
1087 int const c = ((str[0] << 24) & (str[1] << 16) &
1088 (str[2] << 8) & str[3]);
1097 if (!format.empty())
1099 else if (contains(str,"EPSF"))
1100 // dummy, if we have wrong file description like
1101 // %!PS-Adobe-2.0EPSF"
1104 else if (contains(str,"Grace"))
1107 else if (contains(str,"JFIF"))
1110 else if (contains(str,"%PDF"))
1113 else if (contains(str,"PNG"))
1116 else if (contains(str,"%!PS-Adobe")) {
1119 if (contains(str,"EPSF"))
1125 else if (contains(str,"_bits[]"))
1128 else if (contains(str,"XPM") || contains(str, "static char *"))
1131 else if (contains(str,"BITPIX"))
1135 if (!format.empty()) {
1136 lyxerr[Debug::GRAPHICS]
1137 << "Recognised Fileformat: " << format << endl;
1141 lyxerr[Debug::GRAPHICS]
1142 << "filetools(getFormatFromContents)\n"
1143 << "\tCouldn't find a known format!\n";
1148 /// check for zipped file
1149 bool zippedFile(string const & name)
1151 string const type = getFormatFromContents(name);
1152 if (contains("gzip zip compress", type) && !type.empty())
1158 string const unzippedFileName(string const & zipped_file)
1160 string const ext = GetExtension(zipped_file);
1161 if (ext == "gz" || ext == "z" || ext == "Z")
1162 return ChangeExtension(zipped_file, string());
1163 return "unzipped_" + zipped_file;
1167 string const unzipFile(string const & zipped_file, string const & unzipped_file)
1169 string const tempfile = unzipped_file.empty() ?
1170 unzippedFileName(zipped_file) : unzipped_file;
1172 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1174 one.startscript(Systemcall::Wait, command);
1175 // test that command was executed successfully (anon)
1176 // yes, please do. (Lgb)
1181 string const MakeDisplayPath(string const & path, unsigned int threshold)
1185 string const home(package().home_dir());
1187 // replace /home/blah with ~/
1188 if (prefixIs(str, home))
1189 str = subst(str, home, "~");
1191 if (str.length() <= threshold)
1194 string const prefix = ".../";
1197 while (str.length() > threshold)
1198 str = split(str, temp, '/');
1200 // Did we shorten everything away?
1202 // Yes, filename itself is too long.
1203 // Pick the start and the end of the filename.
1204 str = OnlyFilename(path);
1205 string const head = str.substr(0, threshold / 2 - 3);
1207 string::size_type len = str.length();
1209 str.substr(len - threshold / 2 - 2, len - 1);
1210 str = head + "..." + tail;
1213 return prefix + str;
1217 bool LyXReadLink(string const & file, string & link, bool resolve)
1219 #ifdef HAVE_READLINK
1220 char linkbuffer[512];
1221 // Should be PATH_MAX but that needs autconf support
1222 int const nRead = ::readlink(file.c_str(),
1223 linkbuffer, sizeof(linkbuffer) - 1);
1226 linkbuffer[nRead] = '\0'; // terminator
1228 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1238 cmd_ret const RunCommand(string const & cmd)
1240 // FIXME: replace all calls to RunCommand with ForkedCall
1241 // (if the output is not needed) or the code in ispell.C
1242 // (if the output is needed).
1244 // One question is if we should use popen or
1245 // create our own popen based on fork, exec, pipe
1246 // of course the best would be to have a
1247 // pstream (process stream), with the
1248 // variants ipstream, opstream
1250 sigset_t newMask, oldMask;
1251 sigemptyset(&oldMask);
1252 sigemptyset(&newMask);
1253 sigaddset(&newMask, SIGCHLD);
1255 // Block the SIGCHLD signal.
1256 sigprocmask(SIG_BLOCK, &newMask, &oldMask);
1258 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1260 // (Claus Hentschel) Check if popen was succesful ;-)
1262 return make_pair(-1, string());
1263 lyxerr << "RunCommand:: could not start child process" << endl;
1269 ret += static_cast<char>(c);
1272 int const pret = pclose(inf);
1274 perror("RunCommand:: could not terminate child process");
1276 // Unblock the SIGCHLD signal and restore the old mask.
1277 sigprocmask(SIG_SETMASK, &oldMask, 0);
1279 return make_pair(pret, ret);
1283 string const findtexfile(string const & fil, string const & /*format*/)
1285 /* There is no problem to extend this function too use other
1286 methods to look for files. It could be setup to look
1287 in environment paths and also if wanted as a last resort
1288 to a recursive find. One of the easier extensions would
1289 perhaps be to use the LyX file lookup methods. But! I am
1290 going to implement this until I see some demand for it.
1294 // If the file can be found directly, we just return a
1295 // absolute path version of it.
1296 if (FileInfo(fil).exist())
1297 return MakeAbsPath(fil);
1299 // No we try to find it using kpsewhich.
1300 // It seems from the kpsewhich manual page that it is safe to use
1301 // kpsewhich without --format: "When the --format option is not
1302 // given, the search path used when looking for a file is inferred
1303 // from the name given, by looking for a known extension. If no
1304 // known extension is found, the search path for TeX source files
1306 // However, we want to take advantage of the format sine almost all
1307 // the different formats has environment variables that can be used
1308 // to controll which paths to search. f.ex. bib looks in
1309 // BIBINPUTS and TEXBIB. Small list follows:
1310 // bib - BIBINPUTS, TEXBIB
1312 // graphic/figure - TEXPICTS, TEXINPUTS
1313 // ist - TEXINDEXSTYLE, INDEXSTYLE
1314 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1316 // tfm - TFMFONTS, TEXFONTS
1317 // This means that to use kpsewhich in the best possible way we
1318 // should help it by setting additional path in the approp. envir.var.
1319 string const kpsecmd = "kpsewhich " + fil;
1321 cmd_ret const c = RunCommand(kpsecmd);
1323 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1324 << "kpse result = `" << rtrim(c.second, "\n")
1327 return os::internal_path(rtrim(c.second, "\n\r"));
1333 void removeAutosaveFile(string const & filename)
1335 string a = OnlyPath(filename);
1337 a += OnlyFilename(filename);
1339 FileInfo const fileinfo(a);
1340 if (fileinfo.exist())
1345 void readBB_lyxerrMessage(string const & file, bool & zipped,
1346 string const & message)
1348 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1349 << message << std::endl;
1350 #ifdef WITH_WARNINGS
1351 #warning Why is this func deleting a file? (Lgb)
1358 string const readBB_from_PSFile(string const & file)
1360 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1361 // It seems that every command in the header has an own line,
1362 // getline() should work for all files.
1363 // On the other hand some plot programs write the bb at the
1364 // end of the file. Than we have in the header:
1365 // %%BoundingBox: (atend)
1366 // In this case we must check the end.
1367 bool zipped = zippedFile(file);
1368 string const file_ = zipped ?
1369 string(unzipFile(file)) : string(file);
1370 string const format = getFormatFromContents(file_);
1372 if (format != "eps" && format != "ps") {
1373 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1377 std::ifstream is(file_.c_str());
1381 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1382 string const bb = ltrim(s.substr(14));
1383 readBB_lyxerrMessage(file_, zipped, bb);
1387 readBB_lyxerrMessage(file_, zipped, "no bb found");
1392 int compare_timestamps(string const & file1, string const & file2)
1394 BOOST_ASSERT(AbsolutePath(file1) && AbsolutePath(file2));
1396 // If the original is newer than the copy, then copy the original
1397 // to the new directory.
1402 if (f1.exist() && f2.exist()) {
1403 double const tmp = difftime(f1.getModificationTime(),
1404 f2.getModificationTime());
1406 cmp = tmp > 0 ? 1 : -1;
1408 } else if (f1.exist()) {
1410 } else if (f2.exist()) {
1417 } //namespace support