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(lowercase(filename), ".lyx");
92 bool IsSGMLFilename(string const & filename)
94 return suffixIs(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 if (file.isOK() && file.isRegular() && file.readable())
141 // Is a file read_only?
142 // return 1 read-write
144 // -1 error (doesn't exist, no access, anything else)
145 int IsFileWriteable (string const & path)
149 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
151 if (fi.readable()) // read-only
153 return -1; // everything else.
157 //returns true: dir writeable
158 // false: not writeable
159 bool IsDirWriteable (string const & path)
161 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
163 string const tmpfl(lyx::tempName(path, "lyxwritetest"));
173 // Uses a string of paths separated by ";"s to find a file to open.
174 // Can't cope with pathnames with a ';' in them. Returns full path to file.
175 // If path entry begins with $$LyX/, use system_lyxdir
176 // If path entry begins with $$User/, use user_lyxdir
177 // Example: "$$User/doc;$$LyX/doc"
178 string const FileOpenSearch (string const & path, string const & name,
181 string real_file, path_element;
182 bool notfound = true;
183 string tmppath = split(path, path_element, ';');
185 while (notfound && !path_element.empty()) {
186 path_element = os::slashify_path(path_element);
187 if (!suffixIs(path_element, '/'))
189 path_element = subst(path_element, "$$LyX", system_lyxdir);
190 path_element = subst(path_element, "$$User", user_lyxdir);
192 real_file = FileSearch(path_element, name, ext);
194 if (real_file.empty()) {
196 tmppath = split(tmppath, path_element, ';');
197 } while (!tmppath.empty() && path_element.empty());
203 if (ext.empty() && notfound) {
204 real_file = FileOpenSearch(path, name, "exe");
205 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
212 /// Returns a vector of all files in directory dir having extension ext.
213 vector<string> const DirList(string const & dir, string const & ext)
215 // This is a non-error checking C/system implementation
216 string extension(ext);
217 if (!extension.empty() && extension[0] != '.')
218 extension.insert(0, ".");
219 vector<string> dirlist;
220 DIR * dirp = ::opendir(dir.c_str());
223 << "Directory \"" << dir
224 << "\" does not exist to DirList." << endl;
229 while ((dire = ::readdir(dirp))) {
230 string const fil = dire->d_name;
231 if (suffixIs(fil, extension)) {
232 dirlist.push_back(fil);
237 /* I would have prefered to take a vector<string>& as parameter so
238 that we could avoid the copy of the vector when returning.
240 dirlist.swap(argvec);
241 to avoid the copy. (Lgb)
243 /* A C++ implementaion will look like this:
244 string extension(ext);
245 if (extension[0] != '.') extension.insert(0, ".");
246 vector<string> dirlist;
247 directory_iterator dit("dir");
248 while (dit != directory_iterator()) {
249 string fil = dit->filename;
250 if (prefixIs(fil, extension)) {
251 dirlist.push_back(fil);
255 dirlist.swap(argvec);
261 // Returns the real name of file name in directory path, with optional
263 string const FileSearch(string const & path, string const & name,
266 // if `name' is an absolute path, we ignore the setting of `path'
267 // Expand Environmentvariables in 'name'
268 string const tmpname = ReplaceEnvironmentPath(name);
269 string fullname = MakeAbsPath(tmpname, path);
270 // search first without extension, then with it.
271 if (IsFileReadable(fullname))
273 else if (ext.empty())
275 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
278 if (IsFileReadable(fullname))
286 // Search the file name.ext in the subdirectory dir of
288 // 2) build_lyxdir (if not empty)
290 string const LibFileSearch(string const & dir, string const & name,
293 string fullname = FileSearch(AddPath(user_lyxdir, dir), name, ext);
294 if (!fullname.empty())
297 if (!build_lyxdir.empty())
298 fullname = FileSearch(AddPath(build_lyxdir, dir), name, ext);
299 if (!fullname.empty())
302 return FileSearch(AddPath(system_lyxdir, dir), name, ext);
307 i18nLibFileSearch(string const & dir, string const & name,
310 // this comment is from intl/dcigettext.c. We try to mimick this
312 /* The highest priority value is the `LANGUAGE' environment
313 variable. But we don't use the value if the currently
314 selected locale is the C locale. This is a GNU extension. */
316 string const lc_all = GetEnv("LC_ALL");
317 string lang = GetEnv("LANGUAGE");
318 if (lang.empty() || lc_all == "C") {
321 lang = GetEnv("LANG");
325 lang = token(lang, '_', 0);
327 if (lang.empty() || lang == "C")
328 return LibFileSearch(dir, name, ext);
330 string const tmp = LibFileSearch(dir, lang + '_' + name,
335 return LibFileSearch(dir, name, ext);
340 string const LibScriptSearch(string const & command)
343 string args = command;
344 split(args, script, ' ');
345 script = LibFileSearch("scripts", script);
348 else if (args.empty())
351 return script + ' ' + args;
355 string const GetEnv(string const & envname)
357 // f.ex. what about error checking?
358 char const * const ch = getenv(envname.c_str());
359 string const envstr = !ch ? "" : ch;
364 string const GetEnvPath(string const & name)
367 string const pathlist = subst(GetEnv(name), ':', ';');
369 string const pathlist = os::slashify_path(GetEnv(name));
371 return strip(pathlist, ';');
375 bool PutEnv(string const & envstr)
377 // CHECK Look at and fix this.
378 // f.ex. what about error checking?
381 // this leaks, but what can we do about it?
382 // Is doing a getenv() and a free() of the older value
383 // a good idea? (JMarc)
384 // Actually we don't have to leak...calling putenv like this
385 // should be enough: ... and this is obviously not enough if putenv
386 // does not make a copy of the string. It is also not very wise to
387 // put a string on the free store. If we have to leak we should do it
389 char * leaker = new char[envstr.length() + 1];
390 envstr.copy(leaker, envstr.length());
391 leaker[envstr.length()] = '\0';
392 int const retval = lyx::putenv(leaker);
394 // If putenv does not make a copy of the char const * this
395 // is very dangerous. OTOH if it does take a copy this is the
397 // The only implementation of putenv that I have seen does not
398 // allocate memory. _And_ after testing the putenv in glibc it
399 // seems that we need to make a copy of the string contents.
400 // I will enable the above.
401 //int retval = lyx::putenv(envstr.c_str());
405 string const str = envstr.split(varname,'=');
406 int const retval = ::setenv(varname.c_str(), str.c_str(), true);
408 // No environment setting function. Can this happen?
409 int const retval = 1; //return an error condition.
416 bool PutEnvPath(string const & envstr)
418 return PutEnv(envstr);
424 int DeleteAllFilesInDir (string const & path)
426 // I have decided that we will be using parts from the boost
427 // library. Check out http://www.boost.org/
428 // For directory access we will then use the directory_iterator.
429 // Then the code will be something like:
430 // directory_iterator dit(path);
431 // directory_iterator dend;
432 // if (dit == dend) {
433 // Alert::err_alert(_("Error! Cannot open directory:"), path);
436 // for (; dit != dend; ++dit) {
437 // string filename(*dit);
438 // if (filename == "." || filename == "..")
440 // string unlinkpath(AddName(path, filename));
441 // if (lyx::unlink(unlinkpath))
442 // Alert::err_alert(_("Error! Could not remove file:"),
446 DIR * dir = ::opendir(path.c_str());
448 Alert::err_alert (_("Error! Cannot open directory:"), path);
452 int return_value = 0;
453 while ((de = readdir(dir))) {
454 string const temp = de->d_name;
455 if (temp == "." || temp == "..")
457 string const unlinkpath = AddName (path, temp);
459 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
463 FileInfo fi(unlinkpath);
464 if (fi.isOK() && fi.isDir())
465 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
466 deleted &= (lyx::unlink(unlinkpath) == 0);
468 Alert::err_alert(_("Error! Could not remove file:"),
478 string const CreateTmpDir(string const & tempdir, string const & mask)
481 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
482 << "CreateTmpDir: mask=`" << mask << "'" << endl;
484 string const tmpfl(lyx::tempName(tempdir, mask));
485 // lyx::tempName actually creates a file to make sure that it
486 // stays unique. So we have to delete it before we can create
487 // a dir with the same name. Note also that we are not thread
488 // safe because of the gap between unlink and mkdir. (Lgb)
489 lyx::unlink(tmpfl.c_str());
491 if (tmpfl.empty() || lyx::mkdir(tmpfl, 0700)) {
492 Alert::err_alert(_("Error! Couldn't create temporary directory:"),
496 return MakeAbsPath(tmpfl);
500 int DestroyTmpDir(string const & tmpdir, bool Allfiles)
505 if (Allfiles && DeleteAllFilesInDir(tmpdir)) {
508 if (lyx::rmdir(tmpdir)) {
509 Alert::err_alert(_("Error! Couldn't delete temporary directory:"),
519 string const CreateBufferTmpDir(string const & pathfor)
522 static string const tmpdir(pathfor.empty() ? os::getTmpDir() : pathfor);
523 // We are in our own directory. Why bother to mangle name?
524 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
526 string const tmpfl = tmpdir + "/lyx_tmpbuf" + tostr(count++);
527 if (lyx::mkdir(tmpfl, 0777)) {
528 Alert::err_alert(_("Error! Couldn't create temporary directory:"),
536 int DestroyBufferTmpDir(string const & tmpdir)
538 return DestroyTmpDir(tmpdir, true);
542 string const CreateLyXTmpDir(string const & deflt)
544 if ((!deflt.empty()) && (deflt != "/tmp")) {
545 if (lyx::mkdir(deflt, 0777)) {
549 return CreateTmpDir(deflt, "lyx_tmpdir");
556 return CreateTmpDir("/tmp", "lyx_tmpdir");
561 // FIXME: no need for separate method like this ...
562 int DestroyLyXTmpDir(string const & tmpdir)
564 return DestroyTmpDir(tmpdir, true);
568 // Creates directory. Returns true if succesfull
569 bool createDirectory(string const & path, int permission)
571 string temp(strip(os::slashify_path(path), '/'));
574 Alert::alert(_("Internal error!"),
575 _("Call to createDirectory with invalid name"));
579 if (lyx::mkdir(temp, permission)) {
580 Alert::err_alert (_("Error! Couldn't create directory:"), temp);
587 // Strip filename from path name
588 string const OnlyPath(string const & Filename)
590 // If empty filename, return empty
591 if (Filename.empty()) return Filename;
593 // Find last / or start of filename
594 string::size_type j = Filename.rfind('/');
595 if (j == string::npos)
597 return Filename.substr(0, j + 1);
601 // Convert relative path into absolute path based on a basepath.
602 // If relpath is absolute, just use that.
603 // If basepath is empty, use CWD as base.
604 string const MakeAbsPath(string const & RelPath, string const & BasePath)
606 // checks for already absolute path
607 if (os::is_absolute_path(RelPath))
610 // Copies given paths
611 string TempRel(os::slashify_path(RelPath));
612 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
613 TempRel = subst(TempRel, "//", "/");
617 if (os::is_absolute_path(BasePath))
620 TempBase = AddPath(lyx::getcwd(), BasePath);
622 // Handle /./ at the end of the path
623 while (suffixIs(TempBase, "/./"))
624 TempBase.erase(TempBase.length() - 2);
626 // processes relative path
627 string RTemp(TempRel);
630 while (!RTemp.empty()) {
632 RTemp = split(RTemp, Temp, '/');
634 if (Temp == ".") continue;
636 // Remove one level of TempBase
637 string::difference_type i = TempBase.length() - 2;
640 while (i > 0 && TempBase[i] != '/') --i;
644 while (i > 2 && TempBase[i] != '/') --i;
647 TempBase.erase(i, string::npos);
650 } else if (Temp.empty() && !RTemp.empty()) {
651 TempBase = os::current_root() + RTemp;
654 // Add this piece to TempBase
655 if (!suffixIs(TempBase, '/'))
661 // returns absolute path
662 return os::slashify_path(TempBase);
666 // Correctly append filename to the pathname.
667 // If pathname is '.', then don't use pathname.
668 // Chops any path of filename.
669 string const AddName(string const & path, string const & fname)
672 string const basename(OnlyFilename(fname));
676 if (path != "." && path != "./" && !path.empty()) {
677 buf = os::slashify_path(path);
678 if (!suffixIs(path, '/'))
682 return buf + basename;
686 // Strips path from filename
687 string const OnlyFilename(string const & fname)
692 string::size_type j = fname.rfind('/');
693 if (j == string::npos) // no '/' in fname
697 return fname.substr(j + 1);
701 /// Returns true is path is absolute
702 bool AbsolutePath(string const & path)
704 return os::is_absolute_path(path);
709 // Create absolute path. If impossible, don't do anything
710 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
711 string const ExpandPath(string const & path)
713 // checks for already absolute path
714 string RTemp(ReplaceEnvironmentPath(path));
715 if (os::is_absolute_path(RTemp))
719 string const copy(RTemp);
722 RTemp = split(RTemp, Temp, '/');
725 return lyx::getcwd() /*GetCWD()*/ + '/' + RTemp;
728 return GetEnvPath("HOME") + '/' + RTemp;
731 return MakeAbsPath(copy);
733 // Don't know how to handle this
739 // Constracts path/../path
740 // Can't handle "../../" or "/../" (Asger)
741 string const NormalizePath(string const & path)
747 if (os::is_absolute_path(path))
750 // Make implicit current directory explicit
753 while (!RTemp.empty()) {
755 RTemp = split(RTemp, Temp, '/');
759 } else if (Temp == "..") {
760 // Remove one level of TempBase
761 string::difference_type i = TempBase.length() - 2;
762 while (i > 0 && TempBase[i] != '/')
764 if (i >= 0 && TempBase[i] == '/')
765 TempBase.erase(i + 1, string::npos);
769 TempBase += Temp + '/';
773 // returns absolute path
778 string const GetFileContents(string const & fname)
780 FileInfo finfo(fname);
782 ifstream ifs(fname.c_str());
787 return ofs.str().c_str();
790 lyxerr << "LyX was not able to read file '" << fname << "'" << endl;
796 // Search ${...} as Variable-Name inside the string and replace it with
797 // the denoted environmentvariable
798 // Allow Variables according to
799 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
802 string const ReplaceEnvironmentPath(string const & path)
805 // CompareChar: Environmentvariables starts with this character
806 // PathChar: Next path component start with this character
807 // while CompareChar found do:
808 // Split String with PathChar
809 // Search Environmentvariable
810 // if found: Replace Strings
812 char const CompareChar = '$';
813 char const FirstChar = '{';
814 char const EndChar = '}';
815 char const UnderscoreChar = '_';
816 string EndString; EndString += EndChar;
817 string FirstString; FirstString += FirstChar;
818 string CompareString; CompareString += CompareChar;
819 string const RegExp("*}*"); // Exist EndChar inside a String?
821 // first: Search for a '$' - Sign.
823 string result1; //(copy); // for split-calls
824 string result0 = split(path, result1, CompareChar);
825 while (!result0.empty()) {
826 string copy1(result0); // contains String after $
828 // Check, if there is an EndChar inside original String.
830 if (!regexMatch(copy1, RegExp)) {
831 // No EndChar inside. So we are finished
832 result1 += CompareString + result0;
838 string res0 = split(copy1, res1, EndChar);
839 // Now res1 holds the environmentvariable
840 // First, check, if Contents is ok.
841 if (res1.empty()) { // No environmentvariable. Continue Loop.
842 result1 += CompareString + FirstString;
846 // check contents of res1
847 char const * res1_contents = res1.c_str();
848 if (*res1_contents != FirstChar) {
849 // Again No Environmentvariable
850 result1 += CompareString;
854 // Check for variable names
855 // Situation ${} is detected as "No Environmentvariable"
856 char const * cp1 = res1_contents + 1;
857 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
859 while (*cp1 && result) {
860 result = isalnum(*cp1) ||
861 (*cp1 == UnderscoreChar);
866 // no correct variable name
867 result1 += CompareString + res1 + EndString;
868 result0 = split(res0, res1, CompareChar);
873 string env(GetEnv(res1_contents + 1));
875 // Congratulations. Environmentvariable found
878 result1 += CompareString + res1 + EndString;
881 result0 = split(res0, res1, CompareChar);
885 } // ReplaceEnvironmentPath
888 // Make relative path out of two absolute paths
889 string const MakeRelPath(string const & abspath, string const & basepath)
890 // Makes relative path out of absolute path. If it is deeper than basepath,
891 // it's easy. If basepath and abspath share something (they are all deeper
892 // than some directory), it'll be rendered using ..'s. If they are completely
893 // different, then the absolute path will be used as relative path.
895 string::size_type const abslen = abspath.length();
896 string::size_type const baselen = basepath.length();
898 string::size_type i = os::common_path(abspath, basepath);
901 // actually no match - cannot make it relative
905 // Count how many dirs there are in basepath above match
906 // and append as many '..''s into relpath
908 string::size_type j = i;
909 while (j < baselen) {
910 if (basepath[j] == '/') {
911 if (j + 1 == baselen)
918 // Append relative stuff from common directory to abspath
919 if (abspath[i] == '/')
921 for (; i < abslen; ++i)
924 if (suffixIs(buf, '/'))
925 buf.erase(buf.length() - 1);
926 // Substitute empty with .
933 // Append sub-directory(ies) to a path in an intelligent way
934 string const AddPath(string const & path, string const & path_2)
937 string const path2 = os::slashify_path(path_2);
939 if (!path.empty() && path != "." && path != "./") {
940 buf = os::slashify_path(path);
941 if (path[path.length() - 1] != '/')
945 if (!path2.empty()) {
946 string::size_type const p2start = path2.find_first_not_of('/');
947 string::size_type const p2end = path2.find_last_not_of('/');
948 string const tmp = path2.substr(p2start, p2end - p2start + 1);
956 Change extension of oldname to extension.
957 Strips path off if no_path == true.
958 If no extension on oldname, just appends.
960 string const ChangeExtension(string const & oldname, string const & extension)
962 string::size_type const last_slash = oldname.rfind('/');
963 string::size_type last_dot = oldname.rfind('.');
964 if (last_dot < last_slash && last_slash != string::npos)
965 last_dot = string::npos;
968 // Make sure the extension starts with a dot
969 if (!extension.empty() && extension[0] != '.')
970 ext= "." + extension;
974 return os::slashify_path(oldname.substr(0, last_dot) + ext);
978 /// Return the extension of the file (not including the .)
979 string const GetExtension(string const & name)
981 string::size_type const last_slash = name.rfind('/');
982 string::size_type const last_dot = name.rfind('.');
983 if (last_dot != string::npos &&
984 (last_slash == string::npos || last_dot > last_slash))
985 return name.substr(last_dot + 1,
986 name.length() - (last_dot + 1));
991 // the different filetypes and what they contain in one of the first lines
992 // (dots are any characters). (Herbert 20020131)
995 // EPS %!PS-Adobe-3.0 EPSF...
996 // EPSI like EPS and with
1003 // PBM P1... or P4 (B/W)
1004 // PGM P2... or P5 (Grayscale)
1005 // PPM P3... or P6 (color)
1006 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
1007 // SGI \001\332... (decimal 474)
1009 // TIFF II... or MM...
1010 // XBM ..._bits[]...
1011 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
1012 // ...static char *...
1013 // XWD \000\000\000\151 (0x00006900) decimal 105
1015 // GZIP \037\213\010\010... http://www.ietf.org/rfc/rfc1952.txt
1016 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
1017 // Z \037\177 UNIX compress
1019 /// return the "extension" which belongs to the contents.
1020 /// for no knowing contents return the extension. Without
1021 /// an extension and unknown contents we return "user"
1022 string const getExtFromContents(string const & filename)
1025 if (filename.empty() || !IsFileReadable(filename))
1029 ifstream ifs(filename.c_str());
1031 // Couldn't open file...
1035 string const gzipStamp = "\037\213\010\010";
1038 string const zipStamp = "PK";
1041 string const compressStamp = "\037\177";
1043 // Maximum strings to read
1044 int const max_count = 50;
1048 bool firstLine = true;
1049 while ((count++ < max_count) && format.empty()) {
1051 lyxerr[Debug::GRAPHICS]
1052 << "filetools(getExtFromContents)\n"
1053 << "\tFile type not recognised before EOF!"
1060 lyxerr[Debug::GRAPHICS] << "Scanstring: " << str.substr(0,60)
1063 string const stamp = str.substr(0,2);
1064 if (firstLine && str.size() >= 2) {
1065 // at first we check for a zipped file, because this
1066 // information is saved in the first bytes of the file!
1067 // also some graphic formats which save the information
1068 // in the first line, too.
1069 if (prefixIs(str, gzipStamp)) {
1072 } else if (stamp == zipStamp) {
1075 } else if (stamp == compressStamp) {
1076 format = "compress";
1078 // the graphics part
1079 } else if (stamp == "BM") {
1082 } else if (stamp == "\001\332") {
1086 // Don't need to use str.at(0), str.at(1) because
1087 // we already know that str.size() >= 2
1088 } else if (str[0] == 'P') {
1104 } else if ((stamp == "II") || (stamp == "MM")) {
1107 } else if (prefixIs(str,"%TGIF")) {
1110 } else if (prefixIs(str,"GIF")) {
1113 } else if (str.size() > 3) {
1114 int const c = ((str[0] << 24) & (str[1] << 16) &
1115 (str[2] << 8) & str[3]);
1124 if (!format.empty())
1126 else if (contains(str,"EPSF"))
1127 // dummy, if we have wrong file description like
1128 // %!PS-Adobe-2.0EPSF"
1131 else if (contains(str,"Grace"))
1134 else if (contains(str,"JFIF"))
1137 else if (contains(str,"%PDF"))
1140 else if (contains(str,"PNG"))
1143 else if (contains(str,"%!PS-Adobe")) {
1146 if (contains(str,"EPSF"))
1152 else if (contains(str,"_bits[]"))
1155 else if (contains(str,"XPM") || contains(str, "static char *"))
1158 else if (contains(str,"BITPIX"))
1162 if (!format.empty()) {
1163 // if we have eps than epsi is also possible
1164 // we have to check for a preview
1165 if (format == "eps") {
1166 lyxerr[Debug::GRAPHICS]
1167 << "\teps detected -> test for an epsi ..."
1169 while (count++ < max_count) {
1173 if (contains(str, "BeginPreview")) {
1179 lyxerr[Debug::GRAPHICS]
1180 << "Recognised Fileformat: " << format << endl;
1184 string const ext(GetExtension(filename));
1185 lyxerr[Debug::GRAPHICS]
1186 << "filetools(getExtFromContents)\n"
1187 << "\tCouldn't find a known Type!\n";
1189 lyxerr[Debug::GRAPHICS]
1190 << "\twill take the file extension -> "
1194 lyxerr[Debug::GRAPHICS]
1195 << "\twill use ext or a \"user\" defined format" << endl;
1201 /// check for zipped file
1202 bool zippedFile(string const & name)
1204 string const type = getExtFromContents(name);
1205 if (contains("gzip zip compress", type) && !type.empty())
1211 string const unzipFile(string const & zipped_file)
1213 string const file = ChangeExtension(zipped_file, string());
1214 string const tempfile = lyx::tempName(string(), file);
1216 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1218 one.startscript(Systemcall::Wait, command);
1219 // test that command was executed successfully (anon)
1220 // yes, please do. (Lgb)
1225 // Creates a nice compact path for displaying
1227 MakeDisplayPath (string const & path, unsigned int threshold)
1229 string::size_type const l1 = path.length();
1231 // First, we try a relative path compared to home
1232 string const home(GetEnvPath("HOME"));
1233 string relhome = MakeRelPath(path, home);
1235 string::size_type l2 = relhome.length();
1239 // If we backup from home or don't have a relative path,
1240 // this try is no good
1241 if (prefixIs(relhome, "../") || os::is_absolute_path(relhome)) {
1242 // relative path was no good, just use the original path
1249 // Is the path too long?
1250 if (l2 > threshold) {
1256 while (relhome.length() > threshold)
1257 relhome = split(relhome, temp, '/');
1259 // Did we shortend everything away?
1260 if (relhome.empty()) {
1261 // Yes, filename in itself is too long.
1262 // Pick the start and the end of the filename.
1263 relhome = OnlyFilename(path);
1264 string const head = relhome.substr(0, threshold/2 - 3);
1266 l2 = relhome.length();
1268 relhome.substr(l2 - threshold/2 - 2, l2 - 1);
1269 relhome = head + "..." + tail;
1272 return prefix + relhome;
1276 bool LyXReadLink(string const & File, string & Link)
1278 char LinkBuffer[512];
1279 // Should be PATH_MAX but that needs autconf support
1280 int const nRead = ::readlink(File.c_str(),
1281 LinkBuffer, sizeof(LinkBuffer) - 1);
1284 LinkBuffer[nRead] = '\0'; // terminator
1292 typedef pair<int, string> cmdret;
1294 cmdret const do_popen(string const & cmd)
1296 // One question is if we should use popen or
1297 // create our own popen based on fork, exec, pipe
1298 // of course the best would be to have a
1299 // pstream (process stream), with the
1300 // variants ipstream, opstream
1302 FILE * inf = ::popen(cmd.c_str(), os::read_mode());
1304 // (Claus Hentschel) Check if popen was succesful ;-)
1306 return make_pair(-1, string());
1311 ret += static_cast<char>(c);
1314 int const pret = pclose(inf);
1315 return make_pair(pret, ret);
1321 string const findtexfile(string const & fil, string const & /*format*/)
1323 /* There is no problem to extend this function too use other
1324 methods to look for files. It could be setup to look
1325 in environment paths and also if wanted as a last resort
1326 to a recursive find. One of the easier extensions would
1327 perhaps be to use the LyX file lookup methods. But! I am
1328 going to implement this until I see some demand for it.
1332 // If the file can be found directly, we just return a
1333 // absolute path version of it.
1334 if (FileInfo(fil).exist())
1335 return MakeAbsPath(fil);
1337 // No we try to find it using kpsewhich.
1338 // It seems from the kpsewhich manual page that it is safe to use
1339 // kpsewhich without --format: "When the --format option is not
1340 // given, the search path used when looking for a file is inferred
1341 // from the name given, by looking for a known extension. If no
1342 // known extension is found, the search path for TeX source files
1344 // However, we want to take advantage of the format sine almost all
1345 // the different formats has environment variables that can be used
1346 // to controll which paths to search. f.ex. bib looks in
1347 // BIBINPUTS and TEXBIB. Small list follows:
1348 // bib - BIBINPUTS, TEXBIB
1350 // graphic/figure - TEXPICTS, TEXINPUTS
1351 // ist - TEXINDEXSTYLE, INDEXSTYLE
1352 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1354 // tfm - TFMFONTS, TEXFONTS
1355 // This means that to use kpsewhich in the best possible way we
1356 // should help it by setting additional path in the approp. envir.var.
1357 string const kpsecmd = "kpsewhich " + fil;
1359 cmdret const c = do_popen(kpsecmd);
1361 lyxerr[Debug::LATEX] << "kpse status = " << c.first << "\n"
1362 << "kpse result = `" << strip(c.second, '\n')
1365 return os::internal_path(strip(strip(c.second, '\n'), '\r'));
1371 void removeAutosaveFile(string const & filename)
1373 string a = OnlyPath(filename);
1375 a += OnlyFilename(filename);
1377 FileInfo const fileinfo(a);
1378 if (fileinfo.exist()) {
1379 if (lyx::unlink(a) != 0) {
1380 Alert::err_alert(_("Could not delete auto-save file!"), a);
1386 void readBB_lyxerrMessage(string const & file, bool & zipped,
1387 string const & message)
1389 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1390 << message << std::endl;
1396 string const readBB_from_PSFile(string const & file)
1398 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1399 // It seems that every command in the header has an own line,
1400 // getline() should work for all files.
1401 // On the other hand some plot programs write the bb at the
1402 // end of the file. Than we have in the header:
1403 // %%BoundingBox: (atend)
1404 // In this case we must check the end.
1405 bool zipped = zippedFile(file);
1406 string const file_ = zipped ?
1407 string(unzipFile(file)) : string(file);
1408 string const format = getExtFromContents(file_);
1410 if (format != "eps" && format != "ps") {
1411 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1415 std::ifstream is(file_.c_str());
1419 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1420 string const bb = frontStrip(s.substr(14));
1421 readBB_lyxerrMessage(file_, zipped, bb);
1425 readBB_lyxerrMessage(file_, zipped, "no bb found");