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;
84 bool IsLyXFilename(string const & filename)
86 return suffixIs(ascii_lowercase(filename), ".lyx");
90 bool IsSGMLFilename(string const & filename)
92 return suffixIs(ascii_lowercase(filename), ".sgml");
96 // Substitutes spaces with underscores in filename (and path)
97 string const MakeLatexName(string const & file)
99 string name = OnlyFilename(file);
100 string const path = OnlyPath(file);
102 for (string::size_type i = 0; i < name.length(); ++i) {
103 name[i] &= 0x7f; // set 8th bit to 0
106 // ok so we scan through the string twice, but who cares.
107 string const keep("abcdefghijklmnopqrstuvwxyz"
108 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
109 "@!\"'()*+,-./0123456789:;<=>?[]`|");
111 string::size_type pos = 0;
112 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
115 return AddName(path, name);
119 // Substitutes spaces with underscores in filename (and path)
120 string const QuoteName(string const & name)
122 return (os::shell() == os::UNIX) ?
128 // Is a file readable ?
129 bool IsFileReadable(string const & path)
132 return file.isOK() && file.isRegular() && file.readable();
136 // Is a file read_only?
137 // return 1 read-write
139 // -1 error (doesn't exist, no access, anything else)
140 int IsFileWriteable(string const & path)
144 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
146 if (fi.readable()) // read-only
148 return -1; // everything else.
152 //returns true: dir writeable
153 // false: not writeable
154 bool IsDirWriteable(string const & path)
156 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
158 string const tmpfl(lyx::tempName(path, "lyxwritetest"));
168 // Uses a string of paths separated by ";"s to find a file to open.
169 // Can't cope with pathnames with a ';' in them. Returns full path to file.
170 // If path entry begins with $$LyX/, use system_lyxdir
171 // If path entry begins with $$User/, use user_lyxdir
172 // Example: "$$User/doc;$$LyX/doc"
173 string const FileOpenSearch(string const & path, string const & name,
178 bool notfound = true;
179 string tmppath = split(path, path_element, ';');
181 while (notfound && !path_element.empty()) {
182 path_element = os::slashify_path(path_element);
183 if (!suffixIs(path_element, '/'))
185 path_element = subst(path_element, "$$LyX", system_lyxdir);
186 path_element = subst(path_element, "$$User", user_lyxdir);
188 real_file = FileSearch(path_element, name, ext);
190 if (real_file.empty()) {
192 tmppath = split(tmppath, path_element, ';');
193 } while (!tmppath.empty() && path_element.empty());
199 if (ext.empty() && notfound) {
200 real_file = FileOpenSearch(path, name, "exe");
201 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
208 /// Returns a vector of all files in directory dir having extension ext.
209 vector<string> const DirList(string const & dir, string const & ext)
211 // This is a non-error checking C/system implementation
212 string extension(ext);
213 if (!extension.empty() && extension[0] != '.')
214 extension.insert(0, ".");
215 vector<string> dirlist;
216 DIR * dirp = ::opendir(dir.c_str());
219 << "Directory \"" << dir
220 << "\" does not exist to DirList." << endl;
225 while ((dire = ::readdir(dirp))) {
226 string const fil = dire->d_name;
227 if (suffixIs(fil, extension)) {
228 dirlist.push_back(fil);
233 /* I would have prefered to take a vector<string>& as parameter so
234 that we could avoid the copy of the vector when returning.
236 dirlist.swap(argvec);
237 to avoid the copy. (Lgb)
239 /* A C++ implementaion will look like this:
240 string extension(ext);
241 if (extension[0] != '.') extension.insert(0, ".");
242 vector<string> dirlist;
243 directory_iterator dit("dir");
244 while (dit != directory_iterator()) {
245 string fil = dit->filename;
246 if (prefixIs(fil, extension)) {
247 dirlist.push_back(fil);
251 dirlist.swap(argvec);
257 // Returns the real name of file name in directory path, with optional
259 string const FileSearch(string const & path, string const & name,
262 // if `name' is an absolute path, we ignore the setting of `path'
263 // Expand Environmentvariables in 'name'
264 string const tmpname = ReplaceEnvironmentPath(name);
265 string fullname = MakeAbsPath(tmpname, path);
266 // search first without extension, then with it.
267 if (IsFileReadable(fullname))
269 else if (ext.empty())
271 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
274 if (IsFileReadable(fullname))
282 // Search the file name.ext in the subdirectory dir of
284 // 2) build_lyxdir (if not empty)
286 string const LibFileSearch(string const & dir, string const & name,
289 string fullname = FileSearch(AddPath(user_lyxdir, dir), name, ext);
290 if (!fullname.empty())
293 if (!build_lyxdir.empty())
294 fullname = FileSearch(AddPath(build_lyxdir, dir), name, ext);
295 if (!fullname.empty())
298 return FileSearch(AddPath(system_lyxdir, dir), name, ext);
303 i18nLibFileSearch(string const & dir, string const & name,
306 // this comment is from intl/dcigettext.c. We try to mimick this
308 /* The highest priority value is the `LANGUAGE' environment
309 variable. But we don't use the value if the currently
310 selected locale is the C locale. This is a GNU extension. */
312 string const lc_all = GetEnv("LC_ALL");
313 string lang = GetEnv("LANGUAGE");
314 if (lang.empty() || lc_all == "C") {
317 lang = GetEnv("LANG");
321 lang = token(lang, '_', 0);
323 if (lang.empty() || lang == "C")
324 return LibFileSearch(dir, name, ext);
326 string const tmp = LibFileSearch(dir, lang + '_' + name,
331 return LibFileSearch(dir, name, ext);
336 string const LibScriptSearch(string const & command)
339 string args = command;
340 args = split(args, script, ' ');
341 script = LibFileSearch("scripts", script);
344 else if (args.empty())
347 return script + ' ' + args;
351 string const GetEnv(string const & envname)
353 // f.ex. what about error checking?
354 char const * const ch = getenv(envname.c_str());
355 string const envstr = !ch ? "" : ch;
360 string const GetEnvPath(string const & name)
363 string const pathlist = subst(GetEnv(name), ':', ';');
365 string const pathlist = os::slashify_path(GetEnv(name));
367 return rtrim(pathlist, ";");
371 bool PutEnv(string const & envstr)
373 // CHECK Look at and fix this.
374 // f.ex. what about error checking?
377 // this leaks, but what can we do about it?
378 // Is doing a getenv() and a free() of the older value
379 // a good idea? (JMarc)
380 // Actually we don't have to leak...calling putenv like this
381 // should be enough: ... and this is obviously not enough if putenv
382 // does not make a copy of the string. It is also not very wise to
383 // put a string on the free store. If we have to leak we should do it
385 char * leaker = new char[envstr.length() + 1];
386 envstr.copy(leaker, envstr.length());
387 leaker[envstr.length()] = '\0';
388 int const retval = lyx::putenv(leaker);
390 // If putenv does not make a copy of the char const * this
391 // is very dangerous. OTOH if it does take a copy this is the
393 // The only implementation of putenv that I have seen does not
394 // allocate memory. _And_ after testing the putenv in glibc it
395 // seems that we need to make a copy of the string contents.
396 // I will enable the above.
397 //int retval = lyx::putenv(envstr.c_str());
401 string const str = envstr.split(varname,'=');
402 int const retval = ::setenv(varname.c_str(), str.c_str(), true);
404 // No environment setting function. Can this happen?
405 int const retval = 1; //return an error condition.
412 bool PutEnvPath(string const & envstr)
414 return PutEnv(envstr);
420 int DeleteAllFilesInDir(string const & path)
422 // I have decided that we will be using parts from the boost
423 // library. Check out http://www.boost.org/
424 // For directory access we will then use the directory_iterator.
425 // Then the code will be something like:
426 // directory_iterator dit(path);
427 // directory_iterator dend;
428 // if (dit == dend) {
429 // Alert::err_alert(_("Error! Cannot open directory:"), path);
432 // for (; dit != dend; ++dit) {
433 // string filename(*dit);
434 // if (filename == "." || filename == "..")
436 // string unlinkpath(AddName(path, filename));
437 // if (lyx::unlink(unlinkpath))
438 // Alert::err_alert(_("Error! Could not remove file:"),
442 DIR * dir = ::opendir(path.c_str());
444 Alert::err_alert (_("Error! Cannot open directory:"), path);
448 int return_value = 0;
449 while ((de = readdir(dir))) {
450 string const temp = de->d_name;
451 if (temp == "." || temp == "..")
453 string const unlinkpath = AddName (path, temp);
455 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
459 FileInfo fi(unlinkpath);
460 if (fi.isOK() && fi.isDir())
461 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
462 deleted &= (lyx::unlink(unlinkpath) == 0);
464 Alert::err_alert(_("Error! Could not remove file:"),
474 string const CreateTmpDir(string const & tempdir, string const & mask)
477 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
478 << "CreateTmpDir: mask=`" << mask << "'" << endl;
480 string const tmpfl(lyx::tempName(tempdir, mask));
481 // lyx::tempName actually creates a file to make sure that it
482 // stays unique. So we have to delete it before we can create
483 // a dir with the same name. Note also that we are not thread
484 // safe because of the gap between unlink and mkdir. (Lgb)
485 lyx::unlink(tmpfl.c_str());
487 if (tmpfl.empty() || lyx::mkdir(tmpfl, 0700)) {
488 Alert::err_alert(_("Error! Couldn't create temporary directory:"),
492 return MakeAbsPath(tmpfl);
496 int DestroyTmpDir(string const & tmpdir, bool Allfiles)
501 if (Allfiles && DeleteAllFilesInDir(tmpdir)) {
504 if (lyx::rmdir(tmpdir)) {
505 Alert::err_alert(_("Error! Couldn't delete temporary directory:"),
515 string const CreateBufferTmpDir(string const & pathfor)
518 static string const tmpdir(pathfor.empty() ? os::getTmpDir() : pathfor);
519 // We are in our own directory. Why bother to mangle name?
520 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
522 string const tmpfl = tmpdir + "/lyx_tmpbuf" + tostr(count++);
523 if (lyx::mkdir(tmpfl, 0777)) {
524 Alert::err_alert(_("Error! Couldn't create temporary directory:"),
532 int DestroyBufferTmpDir(string const & tmpdir)
534 return DestroyTmpDir(tmpdir, true);
538 string const CreateLyXTmpDir(string const & deflt)
540 if ((!deflt.empty()) && (deflt != "/tmp")) {
541 if (lyx::mkdir(deflt, 0777)) {
545 return CreateTmpDir(deflt, "lyx_tmpdir");
552 return CreateTmpDir("/tmp", "lyx_tmpdir");
557 // FIXME: no need for separate method like this ...
558 int DestroyLyXTmpDir(string const & tmpdir)
560 return DestroyTmpDir(tmpdir, true);
564 // Creates directory. Returns true if succesfull
565 bool createDirectory(string const & path, int permission)
567 string temp(rtrim(os::slashify_path(path), "/"));
570 Alert::alert(_("Internal error!"),
571 _("Call to createDirectory with invalid name"));
575 if (lyx::mkdir(temp, permission)) {
576 Alert::err_alert (_("Error! Couldn't create directory:"), temp);
583 // Strip filename from path name
584 string const OnlyPath(string const & Filename)
586 // If empty filename, return empty
587 if (Filename.empty()) return Filename;
589 // Find last / or start of filename
590 string::size_type j = Filename.rfind('/');
591 if (j == string::npos)
593 return Filename.substr(0, j + 1);
597 // Convert relative path into absolute path based on a basepath.
598 // If relpath is absolute, just use that.
599 // If basepath is empty, use CWD as base.
600 string const MakeAbsPath(string const & RelPath, string const & BasePath)
602 // checks for already absolute path
603 if (os::is_absolute_path(RelPath))
606 // Copies given paths
607 string TempRel(os::slashify_path(RelPath));
608 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
609 TempRel = subst(TempRel, "//", "/");
613 if (os::is_absolute_path(BasePath))
616 TempBase = AddPath(lyx::getcwd(), BasePath);
618 // Handle /./ at the end of the path
619 while (suffixIs(TempBase, "/./"))
620 TempBase.erase(TempBase.length() - 2);
622 // processes relative path
623 string RTemp(TempRel);
626 while (!RTemp.empty()) {
628 RTemp = split(RTemp, Temp, '/');
630 if (Temp == ".") continue;
632 // Remove one level of TempBase
633 string::difference_type i = TempBase.length() - 2;
636 while (i > 0 && TempBase[i] != '/') --i;
640 while (i > 2 && TempBase[i] != '/') --i;
643 TempBase.erase(i, string::npos);
646 } else if (Temp.empty() && !RTemp.empty()) {
647 TempBase = os::current_root() + RTemp;
650 // Add this piece to TempBase
651 if (!suffixIs(TempBase, '/'))
657 // returns absolute path
658 return os::slashify_path(TempBase);
662 // Correctly append filename to the pathname.
663 // If pathname is '.', then don't use pathname.
664 // Chops any path of filename.
665 string const AddName(string const & path, string const & fname)
668 string const basename(OnlyFilename(fname));
672 if (path != "." && path != "./" && !path.empty()) {
673 buf = os::slashify_path(path);
674 if (!suffixIs(path, '/'))
678 return buf + basename;
682 // Strips path from filename
683 string const OnlyFilename(string const & fname)
688 string::size_type j = fname.rfind('/');
689 if (j == string::npos) // no '/' in fname
693 return fname.substr(j + 1);
697 /// Returns true is path is absolute
698 bool AbsolutePath(string const & path)
700 return os::is_absolute_path(path);
705 // Create absolute path. If impossible, don't do anything
706 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
707 string const ExpandPath(string const & path)
709 // checks for already absolute path
710 string RTemp(ReplaceEnvironmentPath(path));
711 if (os::is_absolute_path(RTemp))
715 string const copy(RTemp);
718 RTemp = split(RTemp, Temp, '/');
721 return lyx::getcwd() /*GetCWD()*/ + '/' + RTemp;
724 return GetEnvPath("HOME") + '/' + RTemp;
727 return MakeAbsPath(copy);
729 // Don't know how to handle this
735 // Constracts path/../path
736 // Can't handle "../../" or "/../" (Asger)
737 string const NormalizePath(string const & path)
743 if (os::is_absolute_path(path))
746 // Make implicit current directory explicit
749 while (!RTemp.empty()) {
751 RTemp = split(RTemp, Temp, '/');
755 } else if (Temp == "..") {
756 // Remove one level of TempBase
757 string::difference_type i = TempBase.length() - 2;
758 while (i > 0 && TempBase[i] != '/')
760 if (i >= 0 && TempBase[i] == '/')
761 TempBase.erase(i + 1, string::npos);
765 TempBase += Temp + '/';
769 // returns absolute path
774 string const GetFileContents(string const & fname)
776 FileInfo finfo(fname);
778 ifstream ifs(fname.c_str());
783 return ofs.str().c_str();
786 lyxerr << "LyX was not able to read file '" << fname << "'" << endl;
792 // Search ${...} as Variable-Name inside the string and replace it with
793 // the denoted environmentvariable
794 // Allow Variables according to
795 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
798 string const ReplaceEnvironmentPath(string const & path)
801 // CompareChar: Environmentvariables starts with this character
802 // PathChar: Next path component start with this character
803 // while CompareChar found do:
804 // Split String with PathChar
805 // Search Environmentvariable
806 // if found: Replace Strings
808 char const CompareChar = '$';
809 char const FirstChar = '{';
810 char const EndChar = '}';
811 char const UnderscoreChar = '_';
812 string EndString; EndString += EndChar;
813 string FirstString; FirstString += FirstChar;
814 string CompareString; CompareString += CompareChar;
815 string const RegExp("*}*"); // Exist EndChar inside a String?
817 // first: Search for a '$' - Sign.
819 string result1; //(copy); // for split-calls
820 string result0 = split(path, result1, CompareChar);
821 while (!result0.empty()) {
822 string copy1(result0); // contains String after $
824 // Check, if there is an EndChar inside original String.
826 if (!regexMatch(copy1, RegExp)) {
827 // No EndChar inside. So we are finished
828 result1 += CompareString + result0;
834 string res0 = split(copy1, res1, EndChar);
835 // Now res1 holds the environmentvariable
836 // First, check, if Contents is ok.
837 if (res1.empty()) { // No environmentvariable. Continue Loop.
838 result1 += CompareString + FirstString;
842 // check contents of res1
843 char const * res1_contents = res1.c_str();
844 if (*res1_contents != FirstChar) {
845 // Again No Environmentvariable
846 result1 += CompareString;
850 // Check for variable names
851 // Situation ${} is detected as "No Environmentvariable"
852 char const * cp1 = res1_contents + 1;
853 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
855 while (*cp1 && result) {
856 result = isalnum(*cp1) ||
857 (*cp1 == UnderscoreChar);
862 // no correct variable name
863 result1 += CompareString + res1 + EndString;
864 result0 = split(res0, res1, CompareChar);
869 string env(GetEnv(res1_contents + 1));
871 // Congratulations. Environmentvariable found
874 result1 += CompareString + res1 + EndString;
877 result0 = split(res0, res1, CompareChar);
881 } // ReplaceEnvironmentPath
884 // Make relative path out of two absolute paths
885 string const MakeRelPath(string const & abspath, string const & basepath)
886 // Makes relative path out of absolute path. If it is deeper than basepath,
887 // it's easy. If basepath and abspath share something (they are all deeper
888 // than some directory), it'll be rendered using ..'s. If they are completely
889 // different, then the absolute path will be used as relative path.
891 string::size_type const abslen = abspath.length();
892 string::size_type const baselen = basepath.length();
894 string::size_type i = os::common_path(abspath, basepath);
897 // actually no match - cannot make it relative
901 // Count how many dirs there are in basepath above match
902 // and append as many '..''s into relpath
904 string::size_type j = i;
905 while (j < baselen) {
906 if (basepath[j] == '/') {
907 if (j + 1 == baselen)
914 // Append relative stuff from common directory to abspath
915 if (abspath[i] == '/')
917 for (; i < abslen; ++i)
920 if (suffixIs(buf, '/'))
921 buf.erase(buf.length() - 1);
922 // Substitute empty with .
929 // Append sub-directory(ies) to a path in an intelligent way
930 string const AddPath(string const & path, string const & path_2)
933 string const path2 = os::slashify_path(path_2);
935 if (!path.empty() && path != "." && path != "./") {
936 buf = os::slashify_path(path);
937 if (path[path.length() - 1] != '/')
941 if (!path2.empty()) {
942 string::size_type const p2start = path2.find_first_not_of('/');
943 string::size_type const p2end = path2.find_last_not_of('/');
944 string const tmp = path2.substr(p2start, p2end - p2start + 1);
952 Change extension of oldname to extension.
953 Strips path off if no_path == true.
954 If no extension on oldname, just appends.
956 string const ChangeExtension(string const & oldname, string const & extension)
958 string::size_type const last_slash = oldname.rfind('/');
959 string::size_type last_dot = oldname.rfind('.');
960 if (last_dot < last_slash && last_slash != string::npos)
961 last_dot = string::npos;
964 // Make sure the extension starts with a dot
965 if (!extension.empty() && extension[0] != '.')
966 ext= "." + extension;
970 return os::slashify_path(oldname.substr(0, last_dot) + ext);
974 /// Return the extension of the file (not including the .)
975 string const GetExtension(string const & name)
977 string::size_type const last_slash = name.rfind('/');
978 string::size_type const last_dot = name.rfind('.');
979 if (last_dot != string::npos &&
980 (last_slash == string::npos || last_dot > last_slash))
981 return name.substr(last_dot + 1,
982 name.length() - (last_dot + 1));
987 // the different filetypes and what they contain in one of the first lines
988 // (dots are any characters). (Herbert 20020131)
991 // EPS %!PS-Adobe-3.0 EPSF...
992 // EPSI like EPS and with
1000 // PBM P1... or P4 (B/W)
1001 // PGM P2... or P5 (Grayscale)
1002 // PPM P3... or P6 (color)
1003 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
1004 // SGI \001\332... (decimal 474)
1006 // TIFF II... or MM...
1007 // XBM ..._bits[]...
1008 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
1009 // ...static char *...
1010 // XWD \000\000\000\151 (0x00006900) decimal 105
1012 // GZIP \037\213\010\010... http://www.ietf.org/rfc/rfc1952.txt
1013 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
1014 // Z \037\177 UNIX compress
1016 /// return the "extension" which belongs to the contents.
1017 /// for no knowing contents return the extension. Without
1018 /// an extension and unknown contents we return "user"
1019 string const getExtFromContents(string const & filename)
1022 if (filename.empty() || !IsFileReadable(filename))
1026 ifstream ifs(filename.c_str());
1028 // Couldn't open file...
1032 string const gzipStamp = "\037\213\010\010";
1035 string const zipStamp = "PK";
1038 string const compressStamp = "\037\177";
1040 // Maximum strings to read
1041 int const max_count = 50;
1045 bool firstLine = true;
1046 while ((count++ < max_count) && format.empty()) {
1048 lyxerr[Debug::GRAPHICS]
1049 << "filetools(getExtFromContents)\n"
1050 << "\tFile type not recognised before EOF!"
1056 string const stamp = str.substr(0,2);
1057 if (firstLine && str.size() >= 2) {
1058 // at first we check for a zipped file, because this
1059 // information is saved in the first bytes of the file!
1060 // also some graphic formats which save the information
1061 // in the first line, too.
1062 if (prefixIs(str, gzipStamp)) {
1065 } else if (stamp == zipStamp) {
1068 } else if (stamp == compressStamp) {
1069 format = "compress";
1071 // the graphics part
1072 } else if (stamp == "BM") {
1075 } else if (stamp == "\001\332") {
1079 // Don't need to use str.at(0), str.at(1) because
1080 // we already know that str.size() >= 2
1081 } else if (str[0] == 'P') {
1097 } else if ((stamp == "II") || (stamp == "MM")) {
1100 } else if (prefixIs(str,"%TGIF")) {
1103 } else if (prefixIs(str,"#FIG")) {
1106 } else if (prefixIs(str,"GIF")) {
1109 } else if (str.size() > 3) {
1110 int const c = ((str[0] << 24) & (str[1] << 16) &
1111 (str[2] << 8) & str[3]);
1120 if (!format.empty())
1122 else if (contains(str,"EPSF"))
1123 // dummy, if we have wrong file description like
1124 // %!PS-Adobe-2.0EPSF"
1127 else if (contains(str,"Grace"))
1130 else if (contains(str,"JFIF"))
1133 else if (contains(str,"%PDF"))
1136 else if (contains(str,"PNG"))
1139 else if (contains(str,"%!PS-Adobe")) {
1142 if (contains(str,"EPSF"))
1148 else if (contains(str,"_bits[]"))
1151 else if (contains(str,"XPM") || contains(str, "static char *"))
1154 else if (contains(str,"BITPIX"))
1158 if (!format.empty()) {
1159 // if we have eps than epsi is also possible
1160 // we have to check for a preview
1161 if (format == "eps") {
1162 lyxerr[Debug::GRAPHICS]
1163 << "\teps detected -> test for an epsi ..."
1165 while (count++ < max_count) {
1169 if (contains(str, "BeginPreview")) {
1175 lyxerr[Debug::GRAPHICS]
1176 << "Recognised Fileformat: " << format << endl;
1180 string const ext(GetExtension(filename));
1181 lyxerr[Debug::GRAPHICS]
1182 << "filetools(getExtFromContents)\n"
1183 << "\tCouldn't find a known Type!\n";
1185 lyxerr[Debug::GRAPHICS]
1186 << "\twill take the file extension -> "
1190 lyxerr[Debug::GRAPHICS]
1191 << "\twill use ext or a \"user\" defined format" << endl;
1197 /// check for zipped file
1198 bool zippedFile(string const & name)
1200 string const type = getExtFromContents(name);
1201 if (contains("gzip zip compress", type) && !type.empty())
1207 string const unzipFile(string const & zipped_file)
1209 string const file = ChangeExtension(zipped_file, string());
1210 string const tempfile = lyx::tempName(string(), file);
1212 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1214 one.startscript(Systemcall::Wait, command);
1215 // test that command was executed successfully (anon)
1216 // yes, please do. (Lgb)
1221 // Creates a nice compact path for displaying
1223 MakeDisplayPath (string const & path, unsigned int threshold)
1225 string::size_type const l1 = path.length();
1227 // First, we try a relative path compared to home
1228 string const home(GetEnvPath("HOME"));
1229 string relhome = MakeRelPath(path, home);
1231 string::size_type l2 = relhome.length();
1235 // If we backup from home or don't have a relative path,
1236 // this try is no good
1237 if (prefixIs(relhome, "../") || os::is_absolute_path(relhome)) {
1238 // relative path was no good, just use the original path
1245 // Is the path too long?
1246 if (l2 > threshold) {
1252 while (relhome.length() > threshold)
1253 relhome = split(relhome, temp, '/');
1255 // Did we shortend everything away?
1256 if (relhome.empty()) {
1257 // Yes, filename in itself is too long.
1258 // Pick the start and the end of the filename.
1259 relhome = OnlyFilename(path);
1260 string const head = relhome.substr(0, threshold/2 - 3);
1262 l2 = relhome.length();
1264 relhome.substr(l2 - threshold/2 - 2, l2 - 1);
1265 relhome = head + "..." + tail;
1268 return prefix + relhome;
1272 bool LyXReadLink(string const & file, string & link, bool resolve)
1274 char linkbuffer[512];
1275 // Should be PATH_MAX but that needs autconf support
1276 int const nRead = ::readlink(file.c_str(),
1277 linkbuffer, sizeof(linkbuffer) - 1);
1280 linkbuffer[nRead] = '\0'; // terminator
1282 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1289 cmd_ret const RunCommand(string const & cmd)
1291 // One question is if we should use popen or
1292 // create our own popen based on fork, exec, pipe
1293 // of course the best would be to have a
1294 // pstream (process stream), with the
1295 // variants ipstream, opstream
1297 FILE * inf = ::popen(cmd.c_str(), os::read_mode());
1299 // (Claus Hentschel) Check if popen was succesful ;-)
1301 return make_pair(-1, string());
1306 ret += static_cast<char>(c);
1309 int const pret = pclose(inf);
1310 return make_pair(pret, ret);
1314 string const findtexfile(string const & fil, string const & /*format*/)
1316 /* There is no problem to extend this function too use other
1317 methods to look for files. It could be setup to look
1318 in environment paths and also if wanted as a last resort
1319 to a recursive find. One of the easier extensions would
1320 perhaps be to use the LyX file lookup methods. But! I am
1321 going to implement this until I see some demand for it.
1325 // If the file can be found directly, we just return a
1326 // absolute path version of it.
1327 if (FileInfo(fil).exist())
1328 return MakeAbsPath(fil);
1330 // No we try to find it using kpsewhich.
1331 // It seems from the kpsewhich manual page that it is safe to use
1332 // kpsewhich without --format: "When the --format option is not
1333 // given, the search path used when looking for a file is inferred
1334 // from the name given, by looking for a known extension. If no
1335 // known extension is found, the search path for TeX source files
1337 // However, we want to take advantage of the format sine almost all
1338 // the different formats has environment variables that can be used
1339 // to controll which paths to search. f.ex. bib looks in
1340 // BIBINPUTS and TEXBIB. Small list follows:
1341 // bib - BIBINPUTS, TEXBIB
1343 // graphic/figure - TEXPICTS, TEXINPUTS
1344 // ist - TEXINDEXSTYLE, INDEXSTYLE
1345 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1347 // tfm - TFMFONTS, TEXFONTS
1348 // This means that to use kpsewhich in the best possible way we
1349 // should help it by setting additional path in the approp. envir.var.
1350 string const kpsecmd = "kpsewhich " + fil;
1352 cmd_ret const c = RunCommand(kpsecmd);
1354 lyxerr[Debug::LATEX] << "kpse status = " << c.first << "\n"
1355 << "kpse result = `" << rtrim(c.second, "\n")
1358 return os::internal_path(rtrim(c.second, "\n\r"));
1364 void removeAutosaveFile(string const & filename)
1366 string a = OnlyPath(filename);
1368 a += OnlyFilename(filename);
1370 FileInfo const fileinfo(a);
1371 if (fileinfo.exist()) {
1372 if (lyx::unlink(a) != 0) {
1373 Alert::err_alert(_("Could not delete auto-save file!"), a);
1379 void readBB_lyxerrMessage(string const & file, bool & zipped,
1380 string const & message)
1382 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1383 << message << std::endl;
1389 string const readBB_from_PSFile(string const & file)
1391 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1392 // It seems that every command in the header has an own line,
1393 // getline() should work for all files.
1394 // On the other hand some plot programs write the bb at the
1395 // end of the file. Than we have in the header:
1396 // %%BoundingBox: (atend)
1397 // In this case we must check the end.
1398 bool zipped = zippedFile(file);
1399 string const file_ = zipped ?
1400 string(unzipFile(file)) : string(file);
1401 string const format = getExtFromContents(file_);
1403 if (format != "eps" && format != "ps") {
1404 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1408 std::ifstream is(file_.c_str());
1412 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1413 string const bb = ltrim(s.substr(14));
1414 readBB_lyxerrMessage(file_, zipped, bb);
1418 readBB_lyxerrMessage(file_, zipped, "no bb found");