2 filetools.C (former paths.C) - part of LyX project
3 General path-mangling functions
4 Copyright 1996 Ivan Schreter
5 Parts Copyright 1996 Dirk Niggemann
6 Parts Copyright 1985, 1990, 1993 Free Software Foundation, Inc.
7 Parts Copyright 1996 Asger Alstrup
11 lyx-filetool.C : tools functions for file/path handling
12 this file is part of LyX, the High Level Word Processor
13 Copyright 1995-1996, Matthias Ettrich and the LyX Team
20 #pragma implementation "filetools.h"
24 #include "support/lstrings.h"
25 #include "support/systemcall.h"
27 #include "filetools.h"
29 #include "frontends/Alert.h"
31 #include "support/path.h" // I know it's OS/2 specific (SMiyata)
48 // Which part of this is still necessary? (JMarc).
51 # define NAMLEN(dirent) strlen((dirent)->d_name)
53 # define dirent direct
54 # define NAMLEN(dirent) (dirent)->d_namlen
56 # include <sys/ndir.h>
66 #ifndef CXX_GLOBAL_CSTD
79 extern string system_lyxdir;
80 extern string build_lyxdir;
81 extern string user_lyxdir;
82 extern string system_tempdir;
83 extern string system_packageList;
86 bool IsLyXFilename(string const & filename)
88 return suffixIs(ascii_lowercase(filename), ".lyx");
92 bool IsSGMLFilename(string const & filename)
94 return suffixIs(ascii_lowercase(filename), ".sgml");
98 // Substitutes spaces with underscores in filename (and path)
99 string const MakeLatexName(string const & file)
101 string name = OnlyFilename(file);
102 string const path = OnlyPath(file);
104 for (string::size_type i = 0; i < name.length(); ++i) {
105 name[i] &= 0x7f; // set 8th bit to 0
108 // ok so we scan through the string twice, but who cares.
109 string const keep("abcdefghijklmnopqrstuvwxyz"
110 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
111 "@!\"'()*+,-./0123456789:;<=>?[]`|");
113 string::size_type pos = 0;
114 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
117 return AddName(path, name);
121 // Substitutes spaces with underscores in filename (and path)
122 string const QuoteName(string const & name)
124 return (os::shell() == os::UNIX) ?
130 // Is a file readable ?
131 bool IsFileReadable(string const & path)
134 return file.isOK() && file.isRegular() && file.readable();
138 // Is a file read_only?
139 // return 1 read-write
141 // -1 error (doesn't exist, no access, anything else)
142 int IsFileWriteable(string const & path)
146 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
148 if (fi.readable()) // read-only
150 return -1; // everything else.
154 //returns true: dir writeable
155 // false: not writeable
156 bool IsDirWriteable(string const & path)
158 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
160 string const tmpfl(lyx::tempName(path, "lyxwritetest"));
170 // Uses a string of paths separated by ";"s to find a file to open.
171 // Can't cope with pathnames with a ';' in them. Returns full path to file.
172 // If path entry begins with $$LyX/, use system_lyxdir
173 // If path entry begins with $$User/, use user_lyxdir
174 // Example: "$$User/doc;$$LyX/doc"
175 string const FileOpenSearch(string const & path, string const & name,
180 bool notfound = true;
181 string tmppath = split(path, path_element, ';');
183 while (notfound && !path_element.empty()) {
184 path_element = os::slashify_path(path_element);
185 if (!suffixIs(path_element, '/'))
187 path_element = subst(path_element, "$$LyX", system_lyxdir);
188 path_element = subst(path_element, "$$User", user_lyxdir);
190 real_file = FileSearch(path_element, name, ext);
192 if (real_file.empty()) {
194 tmppath = split(tmppath, path_element, ';');
195 } while (!tmppath.empty() && path_element.empty());
201 if (ext.empty() && notfound) {
202 real_file = FileOpenSearch(path, name, "exe");
203 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
210 /// Returns a vector of all files in directory dir having extension ext.
211 vector<string> const DirList(string const & dir, string const & ext)
213 // This is a non-error checking C/system implementation
214 string extension(ext);
215 if (!extension.empty() && extension[0] != '.')
216 extension.insert(0, ".");
217 vector<string> dirlist;
218 DIR * dirp = ::opendir(dir.c_str());
221 << "Directory \"" << dir
222 << "\" does not exist to DirList." << endl;
227 while ((dire = ::readdir(dirp))) {
228 string const fil = dire->d_name;
229 if (suffixIs(fil, extension)) {
230 dirlist.push_back(fil);
235 /* I would have prefered to take a vector<string>& as parameter so
236 that we could avoid the copy of the vector when returning.
238 dirlist.swap(argvec);
239 to avoid the copy. (Lgb)
241 /* A C++ implementaion will look like this:
242 string extension(ext);
243 if (extension[0] != '.') extension.insert(0, ".");
244 vector<string> dirlist;
245 directory_iterator dit("dir");
246 while (dit != directory_iterator()) {
247 string fil = dit->filename;
248 if (prefixIs(fil, extension)) {
249 dirlist.push_back(fil);
253 dirlist.swap(argvec);
259 // Returns the real name of file name in directory path, with optional
261 string const FileSearch(string const & path, string const & name,
264 // if `name' is an absolute path, we ignore the setting of `path'
265 // Expand Environmentvariables in 'name'
266 string const tmpname = ReplaceEnvironmentPath(name);
267 string fullname = MakeAbsPath(tmpname, path);
268 // search first without extension, then with it.
269 if (IsFileReadable(fullname))
271 else if (ext.empty())
273 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
276 if (IsFileReadable(fullname))
284 // Search the file name.ext in the subdirectory dir of
286 // 2) build_lyxdir (if not empty)
288 string const LibFileSearch(string const & dir, string const & name,
291 string fullname = FileSearch(AddPath(user_lyxdir, dir), name, ext);
292 if (!fullname.empty())
295 if (!build_lyxdir.empty())
296 fullname = FileSearch(AddPath(build_lyxdir, dir), name, ext);
297 if (!fullname.empty())
300 return FileSearch(AddPath(system_lyxdir, dir), name, ext);
305 i18nLibFileSearch(string const & dir, string const & name,
308 // this comment is from intl/dcigettext.c. We try to mimick this
310 /* The highest priority value is the `LANGUAGE' environment
311 variable. But we don't use the value if the currently
312 selected locale is the C locale. This is a GNU extension. */
314 string const lc_all = GetEnv("LC_ALL");
315 string lang = GetEnv("LANGUAGE");
316 if (lang.empty() || lc_all == "C") {
319 lang = GetEnv("LANG");
323 lang = token(lang, '_', 0);
325 if (lang.empty() || lang == "C")
326 return LibFileSearch(dir, name, ext);
328 string const tmp = LibFileSearch(dir, lang + '_' + name,
333 return LibFileSearch(dir, name, ext);
338 string const LibScriptSearch(string const & command)
341 string args = command;
342 split(args, script, ' ');
343 script = LibFileSearch("scripts", script);
346 else if (args.empty())
349 return script + ' ' + args;
353 string const GetEnv(string const & envname)
355 // f.ex. what about error checking?
356 char const * const ch = getenv(envname.c_str());
357 string const envstr = !ch ? "" : ch;
362 string const GetEnvPath(string const & name)
365 string const pathlist = subst(GetEnv(name), ':', ';');
367 string const pathlist = os::slashify_path(GetEnv(name));
369 return strip(pathlist, ';');
373 bool PutEnv(string const & envstr)
375 // CHECK Look at and fix this.
376 // f.ex. what about error checking?
379 // this leaks, but what can we do about it?
380 // Is doing a getenv() and a free() of the older value
381 // a good idea? (JMarc)
382 // Actually we don't have to leak...calling putenv like this
383 // should be enough: ... and this is obviously not enough if putenv
384 // does not make a copy of the string. It is also not very wise to
385 // put a string on the free store. If we have to leak we should do it
387 char * leaker = new char[envstr.length() + 1];
388 envstr.copy(leaker, envstr.length());
389 leaker[envstr.length()] = '\0';
390 int const retval = lyx::putenv(leaker);
392 // If putenv does not make a copy of the char const * this
393 // is very dangerous. OTOH if it does take a copy this is the
395 // The only implementation of putenv that I have seen does not
396 // allocate memory. _And_ after testing the putenv in glibc it
397 // seems that we need to make a copy of the string contents.
398 // I will enable the above.
399 //int retval = lyx::putenv(envstr.c_str());
403 string const str = envstr.split(varname,'=');
404 int const retval = ::setenv(varname.c_str(), str.c_str(), true);
406 // No environment setting function. Can this happen?
407 int const retval = 1; //return an error condition.
414 bool PutEnvPath(string const & envstr)
416 return PutEnv(envstr);
422 int DeleteAllFilesInDir(string const & path)
424 // I have decided that we will be using parts from the boost
425 // library. Check out http://www.boost.org/
426 // For directory access we will then use the directory_iterator.
427 // Then the code will be something like:
428 // directory_iterator dit(path);
429 // directory_iterator dend;
430 // if (dit == dend) {
431 // Alert::err_alert(_("Error! Cannot open directory:"), path);
434 // for (; dit != dend; ++dit) {
435 // string filename(*dit);
436 // if (filename == "." || filename == "..")
438 // string unlinkpath(AddName(path, filename));
439 // if (lyx::unlink(unlinkpath))
440 // Alert::err_alert(_("Error! Could not remove file:"),
444 DIR * dir = ::opendir(path.c_str());
446 Alert::err_alert (_("Error! Cannot open directory:"), path);
450 int return_value = 0;
451 while ((de = readdir(dir))) {
452 string const temp = de->d_name;
453 if (temp == "." || temp == "..")
455 string const unlinkpath = AddName (path, temp);
457 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
461 FileInfo fi(unlinkpath);
462 if (fi.isOK() && fi.isDir())
463 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
464 deleted &= (lyx::unlink(unlinkpath) == 0);
466 Alert::err_alert(_("Error! Could not remove file:"),
476 string const CreateTmpDir(string const & tempdir, string const & mask)
479 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
480 << "CreateTmpDir: mask=`" << mask << "'" << endl;
482 string const tmpfl(lyx::tempName(tempdir, mask));
483 // lyx::tempName actually creates a file to make sure that it
484 // stays unique. So we have to delete it before we can create
485 // a dir with the same name. Note also that we are not thread
486 // safe because of the gap between unlink and mkdir. (Lgb)
487 lyx::unlink(tmpfl.c_str());
489 if (tmpfl.empty() || lyx::mkdir(tmpfl, 0700)) {
490 Alert::err_alert(_("Error! Couldn't create temporary directory:"),
494 return MakeAbsPath(tmpfl);
498 int DestroyTmpDir(string const & tmpdir, bool Allfiles)
503 if (Allfiles && DeleteAllFilesInDir(tmpdir)) {
506 if (lyx::rmdir(tmpdir)) {
507 Alert::err_alert(_("Error! Couldn't delete temporary directory:"),
517 string const CreateBufferTmpDir(string const & pathfor)
520 static string const tmpdir(pathfor.empty() ? os::getTmpDir() : pathfor);
521 // We are in our own directory. Why bother to mangle name?
522 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
524 string const tmpfl = tmpdir + "/lyx_tmpbuf" + tostr(count++);
525 if (lyx::mkdir(tmpfl, 0777)) {
526 Alert::err_alert(_("Error! Couldn't create temporary directory:"),
534 int DestroyBufferTmpDir(string const & tmpdir)
536 return DestroyTmpDir(tmpdir, true);
540 string const CreateLyXTmpDir(string const & deflt)
542 if ((!deflt.empty()) && (deflt != "/tmp")) {
543 if (lyx::mkdir(deflt, 0777)) {
547 return CreateTmpDir(deflt, "lyx_tmpdir");
554 return CreateTmpDir("/tmp", "lyx_tmpdir");
559 // FIXME: no need for separate method like this ...
560 int DestroyLyXTmpDir(string const & tmpdir)
562 return DestroyTmpDir(tmpdir, true);
566 // Creates directory. Returns true if succesfull
567 bool createDirectory(string const & path, int permission)
569 string temp(strip(os::slashify_path(path), '/'));
572 Alert::alert(_("Internal error!"),
573 _("Call to createDirectory with invalid name"));
577 if (lyx::mkdir(temp, permission)) {
578 Alert::err_alert (_("Error! Couldn't create directory:"), temp);
585 // Strip filename from path name
586 string const OnlyPath(string const & Filename)
588 // If empty filename, return empty
589 if (Filename.empty()) return Filename;
591 // Find last / or start of filename
592 string::size_type j = Filename.rfind('/');
593 if (j == string::npos)
595 return Filename.substr(0, j + 1);
599 // Convert relative path into absolute path based on a basepath.
600 // If relpath is absolute, just use that.
601 // If basepath is empty, use CWD as base.
602 string const MakeAbsPath(string const & RelPath, string const & BasePath)
604 // checks for already absolute path
605 if (os::is_absolute_path(RelPath))
608 // Copies given paths
609 string TempRel(os::slashify_path(RelPath));
610 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
611 TempRel = subst(TempRel, "//", "/");
615 if (os::is_absolute_path(BasePath))
618 TempBase = AddPath(lyx::getcwd(), BasePath);
620 // Handle /./ at the end of the path
621 while (suffixIs(TempBase, "/./"))
622 TempBase.erase(TempBase.length() - 2);
624 // processes relative path
625 string RTemp(TempRel);
628 while (!RTemp.empty()) {
630 RTemp = split(RTemp, Temp, '/');
632 if (Temp == ".") continue;
634 // Remove one level of TempBase
635 string::difference_type i = TempBase.length() - 2;
638 while (i > 0 && TempBase[i] != '/') --i;
642 while (i > 2 && TempBase[i] != '/') --i;
645 TempBase.erase(i, string::npos);
648 } else if (Temp.empty() && !RTemp.empty()) {
649 TempBase = os::current_root() + RTemp;
652 // Add this piece to TempBase
653 if (!suffixIs(TempBase, '/'))
659 // returns absolute path
660 return os::slashify_path(TempBase);
664 // Correctly append filename to the pathname.
665 // If pathname is '.', then don't use pathname.
666 // Chops any path of filename.
667 string const AddName(string const & path, string const & fname)
670 string const basename(OnlyFilename(fname));
674 if (path != "." && path != "./" && !path.empty()) {
675 buf = os::slashify_path(path);
676 if (!suffixIs(path, '/'))
680 return buf + basename;
684 // Strips path from filename
685 string const OnlyFilename(string const & fname)
690 string::size_type j = fname.rfind('/');
691 if (j == string::npos) // no '/' in fname
695 return fname.substr(j + 1);
699 /// Returns true is path is absolute
700 bool AbsolutePath(string const & path)
702 return os::is_absolute_path(path);
707 // Create absolute path. If impossible, don't do anything
708 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
709 string const ExpandPath(string const & path)
711 // checks for already absolute path
712 string RTemp(ReplaceEnvironmentPath(path));
713 if (os::is_absolute_path(RTemp))
717 string const copy(RTemp);
720 RTemp = split(RTemp, Temp, '/');
723 return lyx::getcwd() /*GetCWD()*/ + '/' + RTemp;
726 return GetEnvPath("HOME") + '/' + RTemp;
729 return MakeAbsPath(copy);
731 // Don't know how to handle this
737 // Constracts path/../path
738 // Can't handle "../../" or "/../" (Asger)
739 string const NormalizePath(string const & path)
745 if (os::is_absolute_path(path))
748 // Make implicit current directory explicit
751 while (!RTemp.empty()) {
753 RTemp = split(RTemp, Temp, '/');
757 } else if (Temp == "..") {
758 // Remove one level of TempBase
759 string::difference_type i = TempBase.length() - 2;
760 while (i > 0 && TempBase[i] != '/')
762 if (i >= 0 && TempBase[i] == '/')
763 TempBase.erase(i + 1, string::npos);
767 TempBase += Temp + '/';
771 // returns absolute path
776 string const GetFileContents(string const & fname)
778 FileInfo finfo(fname);
780 ifstream ifs(fname.c_str());
785 return ofs.str().c_str();
788 lyxerr << "LyX was not able to read file '" << fname << "'" << endl;
794 // Search ${...} as Variable-Name inside the string and replace it with
795 // the denoted environmentvariable
796 // Allow Variables according to
797 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
800 string const ReplaceEnvironmentPath(string const & path)
803 // CompareChar: Environmentvariables starts with this character
804 // PathChar: Next path component start with this character
805 // while CompareChar found do:
806 // Split String with PathChar
807 // Search Environmentvariable
808 // if found: Replace Strings
810 char const CompareChar = '$';
811 char const FirstChar = '{';
812 char const EndChar = '}';
813 char const UnderscoreChar = '_';
814 string EndString; EndString += EndChar;
815 string FirstString; FirstString += FirstChar;
816 string CompareString; CompareString += CompareChar;
817 string const RegExp("*}*"); // Exist EndChar inside a String?
819 // first: Search for a '$' - Sign.
821 string result1; //(copy); // for split-calls
822 string result0 = split(path, result1, CompareChar);
823 while (!result0.empty()) {
824 string copy1(result0); // contains String after $
826 // Check, if there is an EndChar inside original String.
828 if (!regexMatch(copy1, RegExp)) {
829 // No EndChar inside. So we are finished
830 result1 += CompareString + result0;
836 string res0 = split(copy1, res1, EndChar);
837 // Now res1 holds the environmentvariable
838 // First, check, if Contents is ok.
839 if (res1.empty()) { // No environmentvariable. Continue Loop.
840 result1 += CompareString + FirstString;
844 // check contents of res1
845 char const * res1_contents = res1.c_str();
846 if (*res1_contents != FirstChar) {
847 // Again No Environmentvariable
848 result1 += CompareString;
852 // Check for variable names
853 // Situation ${} is detected as "No Environmentvariable"
854 char const * cp1 = res1_contents + 1;
855 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
857 while (*cp1 && result) {
858 result = isalnum(*cp1) ||
859 (*cp1 == UnderscoreChar);
864 // no correct variable name
865 result1 += CompareString + res1 + EndString;
866 result0 = split(res0, res1, CompareChar);
871 string env(GetEnv(res1_contents + 1));
873 // Congratulations. Environmentvariable found
876 result1 += CompareString + res1 + EndString;
879 result0 = split(res0, res1, CompareChar);
883 } // ReplaceEnvironmentPath
886 // Make relative path out of two absolute paths
887 string const MakeRelPath(string const & abspath, string const & basepath)
888 // Makes relative path out of absolute path. If it is deeper than basepath,
889 // it's easy. If basepath and abspath share something (they are all deeper
890 // than some directory), it'll be rendered using ..'s. If they are completely
891 // different, then the absolute path will be used as relative path.
893 string::size_type const abslen = abspath.length();
894 string::size_type const baselen = basepath.length();
896 string::size_type i = os::common_path(abspath, basepath);
899 // actually no match - cannot make it relative
903 // Count how many dirs there are in basepath above match
904 // and append as many '..''s into relpath
906 string::size_type j = i;
907 while (j < baselen) {
908 if (basepath[j] == '/') {
909 if (j + 1 == baselen)
916 // Append relative stuff from common directory to abspath
917 if (abspath[i] == '/')
919 for (; i < abslen; ++i)
922 if (suffixIs(buf, '/'))
923 buf.erase(buf.length() - 1);
924 // Substitute empty with .
931 // Append sub-directory(ies) to a path in an intelligent way
932 string const AddPath(string const & path, string const & path_2)
935 string const path2 = os::slashify_path(path_2);
937 if (!path.empty() && path != "." && path != "./") {
938 buf = os::slashify_path(path);
939 if (path[path.length() - 1] != '/')
943 if (!path2.empty()) {
944 string::size_type const p2start = path2.find_first_not_of('/');
945 string::size_type const p2end = path2.find_last_not_of('/');
946 string const tmp = path2.substr(p2start, p2end - p2start + 1);
954 Change extension of oldname to extension.
955 Strips path off if no_path == true.
956 If no extension on oldname, just appends.
958 string const ChangeExtension(string const & oldname, string const & extension)
960 string::size_type const last_slash = oldname.rfind('/');
961 string::size_type last_dot = oldname.rfind('.');
962 if (last_dot < last_slash && last_slash != string::npos)
963 last_dot = string::npos;
966 // Make sure the extension starts with a dot
967 if (!extension.empty() && extension[0] != '.')
968 ext= "." + extension;
972 return os::slashify_path(oldname.substr(0, last_dot) + ext);
976 /// Return the extension of the file (not including the .)
977 string const GetExtension(string const & name)
979 string::size_type const last_slash = name.rfind('/');
980 string::size_type const last_dot = name.rfind('.');
981 if (last_dot != string::npos &&
982 (last_slash == string::npos || last_dot > last_slash))
983 return name.substr(last_dot + 1,
984 name.length() - (last_dot + 1));
989 // the different filetypes and what they contain in one of the first lines
990 // (dots are any characters). (Herbert 20020131)
993 // EPS %!PS-Adobe-3.0 EPSF...
994 // EPSI like EPS and with
1002 // PBM P1... or P4 (B/W)
1003 // PGM P2... or P5 (Grayscale)
1004 // PPM P3... or P6 (color)
1005 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
1006 // SGI \001\332... (decimal 474)
1008 // TIFF II... or MM...
1009 // XBM ..._bits[]...
1010 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
1011 // ...static char *...
1012 // XWD \000\000\000\151 (0x00006900) decimal 105
1014 // GZIP \037\213\010\010... http://www.ietf.org/rfc/rfc1952.txt
1015 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
1016 // Z \037\177 UNIX compress
1018 /// return the "extension" which belongs to the contents.
1019 /// for no knowing contents return the extension. Without
1020 /// an extension and unknown contents we return "user"
1021 string const getExtFromContents(string const & filename)
1024 if (filename.empty() || !IsFileReadable(filename))
1028 ifstream ifs(filename.c_str());
1030 // Couldn't open file...
1034 string const gzipStamp = "\037\213\010\010";
1037 string const zipStamp = "PK";
1040 string const compressStamp = "\037\177";
1042 // Maximum strings to read
1043 int const max_count = 50;
1047 bool firstLine = true;
1048 while ((count++ < max_count) && format.empty()) {
1050 lyxerr[Debug::GRAPHICS]
1051 << "filetools(getExtFromContents)\n"
1052 << "\tFile type not recognised before EOF!"
1058 string const stamp = str.substr(0,2);
1059 if (firstLine && str.size() >= 2) {
1060 // at first we check for a zipped file, because this
1061 // information is saved in the first bytes of the file!
1062 // also some graphic formats which save the information
1063 // in the first line, too.
1064 if (prefixIs(str, gzipStamp)) {
1067 } else if (stamp == zipStamp) {
1070 } else if (stamp == compressStamp) {
1071 format = "compress";
1073 // the graphics part
1074 } else if (stamp == "BM") {
1077 } else if (stamp == "\001\332") {
1081 // Don't need to use str.at(0), str.at(1) because
1082 // we already know that str.size() >= 2
1083 } else if (str[0] == 'P') {
1099 } else if ((stamp == "II") || (stamp == "MM")) {
1102 } else if (prefixIs(str,"%TGIF")) {
1105 } else if (prefixIs(str,"#FIG")) {
1108 } else if (prefixIs(str,"GIF")) {
1111 } else if (str.size() > 3) {
1112 int const c = ((str[0] << 24) & (str[1] << 16) &
1113 (str[2] << 8) & str[3]);
1122 if (!format.empty())
1124 else if (contains(str,"EPSF"))
1125 // dummy, if we have wrong file description like
1126 // %!PS-Adobe-2.0EPSF"
1129 else if (contains(str,"Grace"))
1132 else if (contains(str,"JFIF"))
1135 else if (contains(str,"%PDF"))
1138 else if (contains(str,"PNG"))
1141 else if (contains(str,"%!PS-Adobe")) {
1144 if (contains(str,"EPSF"))
1150 else if (contains(str,"_bits[]"))
1153 else if (contains(str,"XPM") || contains(str, "static char *"))
1156 else if (contains(str,"BITPIX"))
1160 if (!format.empty()) {
1161 // if we have eps than epsi is also possible
1162 // we have to check for a preview
1163 if (format == "eps") {
1164 lyxerr[Debug::GRAPHICS]
1165 << "\teps detected -> test for an epsi ..."
1167 while (count++ < max_count) {
1171 if (contains(str, "BeginPreview")) {
1177 lyxerr[Debug::GRAPHICS]
1178 << "Recognised Fileformat: " << format << endl;
1182 string const ext(GetExtension(filename));
1183 lyxerr[Debug::GRAPHICS]
1184 << "filetools(getExtFromContents)\n"
1185 << "\tCouldn't find a known Type!\n";
1187 lyxerr[Debug::GRAPHICS]
1188 << "\twill take the file extension -> "
1192 lyxerr[Debug::GRAPHICS]
1193 << "\twill use ext or a \"user\" defined format" << endl;
1199 /// check for zipped file
1200 bool zippedFile(string const & name)
1202 string const type = getExtFromContents(name);
1203 if (contains("gzip zip compress", type) && !type.empty())
1209 string const unzipFile(string const & zipped_file)
1211 string const file = ChangeExtension(zipped_file, string());
1212 string const tempfile = lyx::tempName(string(), file);
1214 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1216 one.startscript(Systemcall::Wait, command);
1217 // test that command was executed successfully (anon)
1218 // yes, please do. (Lgb)
1223 // Creates a nice compact path for displaying
1225 MakeDisplayPath (string const & path, unsigned int threshold)
1227 string::size_type const l1 = path.length();
1229 // First, we try a relative path compared to home
1230 string const home(GetEnvPath("HOME"));
1231 string relhome = MakeRelPath(path, home);
1233 string::size_type l2 = relhome.length();
1237 // If we backup from home or don't have a relative path,
1238 // this try is no good
1239 if (prefixIs(relhome, "../") || os::is_absolute_path(relhome)) {
1240 // relative path was no good, just use the original path
1247 // Is the path too long?
1248 if (l2 > threshold) {
1254 while (relhome.length() > threshold)
1255 relhome = split(relhome, temp, '/');
1257 // Did we shortend everything away?
1258 if (relhome.empty()) {
1259 // Yes, filename in itself is too long.
1260 // Pick the start and the end of the filename.
1261 relhome = OnlyFilename(path);
1262 string const head = relhome.substr(0, threshold/2 - 3);
1264 l2 = relhome.length();
1266 relhome.substr(l2 - threshold/2 - 2, l2 - 1);
1267 relhome = head + "..." + tail;
1270 return prefix + relhome;
1274 bool LyXReadLink(string const & File, string & Link)
1276 char LinkBuffer[512];
1277 // Should be PATH_MAX but that needs autconf support
1278 int const nRead = ::readlink(File.c_str(),
1279 LinkBuffer, sizeof(LinkBuffer) - 1);
1282 LinkBuffer[nRead] = '\0'; // terminator
1290 typedef pair<int, string> cmdret;
1292 cmdret const do_popen(string const & cmd)
1294 // One question is if we should use popen or
1295 // create our own popen based on fork, exec, pipe
1296 // of course the best would be to have a
1297 // pstream (process stream), with the
1298 // variants ipstream, opstream
1300 FILE * inf = ::popen(cmd.c_str(), os::read_mode());
1302 // (Claus Hentschel) Check if popen was succesful ;-)
1304 return make_pair(-1, string());
1309 ret += static_cast<char>(c);
1312 int const pret = pclose(inf);
1313 return make_pair(pret, ret);
1319 string const findtexfile(string const & fil, string const & /*format*/)
1321 /* There is no problem to extend this function too use other
1322 methods to look for files. It could be setup to look
1323 in environment paths and also if wanted as a last resort
1324 to a recursive find. One of the easier extensions would
1325 perhaps be to use the LyX file lookup methods. But! I am
1326 going to implement this until I see some demand for it.
1330 // If the file can be found directly, we just return a
1331 // absolute path version of it.
1332 if (FileInfo(fil).exist())
1333 return MakeAbsPath(fil);
1335 // No we try to find it using kpsewhich.
1336 // It seems from the kpsewhich manual page that it is safe to use
1337 // kpsewhich without --format: "When the --format option is not
1338 // given, the search path used when looking for a file is inferred
1339 // from the name given, by looking for a known extension. If no
1340 // known extension is found, the search path for TeX source files
1342 // However, we want to take advantage of the format sine almost all
1343 // the different formats has environment variables that can be used
1344 // to controll which paths to search. f.ex. bib looks in
1345 // BIBINPUTS and TEXBIB. Small list follows:
1346 // bib - BIBINPUTS, TEXBIB
1348 // graphic/figure - TEXPICTS, TEXINPUTS
1349 // ist - TEXINDEXSTYLE, INDEXSTYLE
1350 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1352 // tfm - TFMFONTS, TEXFONTS
1353 // This means that to use kpsewhich in the best possible way we
1354 // should help it by setting additional path in the approp. envir.var.
1355 string const kpsecmd = "kpsewhich " + fil;
1357 cmdret const c = do_popen(kpsecmd);
1359 lyxerr[Debug::LATEX] << "kpse status = " << c.first << "\n"
1360 << "kpse result = `" << strip(c.second, '\n')
1363 return os::internal_path(strip(strip(c.second, '\n'), '\r'));
1369 void removeAutosaveFile(string const & filename)
1371 string a = OnlyPath(filename);
1373 a += OnlyFilename(filename);
1375 FileInfo const fileinfo(a);
1376 if (fileinfo.exist()) {
1377 if (lyx::unlink(a) != 0) {
1378 Alert::err_alert(_("Could not delete auto-save file!"), a);
1384 void readBB_lyxerrMessage(string const & file, bool & zipped,
1385 string const & message)
1387 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1388 << message << std::endl;
1394 string const readBB_from_PSFile(string const & file)
1396 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1397 // It seems that every command in the header has an own line,
1398 // getline() should work for all files.
1399 // On the other hand some plot programs write the bb at the
1400 // end of the file. Than we have in the header:
1401 // %%BoundingBox: (atend)
1402 // In this case we must check the end.
1403 bool zipped = zippedFile(file);
1404 string const file_ = zipped ?
1405 string(unzipFile(file)) : string(file);
1406 string const format = getExtFromContents(file_);
1408 if (format != "eps" && format != "ps") {
1409 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1413 std::ifstream is(file_.c_str());
1417 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1418 string const bb = frontStrip(s.substr(14));
1419 readBB_lyxerrMessage(file_, zipped, bb);
1423 readBB_lyxerrMessage(file_, zipped, "no bb found");