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
27 #pragma implementation "filetools.h"
35 #include "support/lstrings.h"
36 #include "support/systemcall.h"
38 #include "filetools.h"
39 #include "LSubstring.h"
40 #include "frontends/Alert.h"
42 #include "support/path.h" // I know it's OS/2 specific (SMiyata)
47 // Which part of this is still necessary? (JMarc).
50 # define NAMLEN(dirent) strlen((dirent)->d_name)
52 # define dirent direct
53 # define NAMLEN(dirent) (dirent)->d_namlen
55 # include <sys/ndir.h>
72 extern string system_lyxdir;
73 extern string build_lyxdir;
74 extern string user_lyxdir;
75 extern string system_tempdir;
76 extern string system_packageList;
79 bool IsLyXFilename(string const & filename)
81 return suffixIs(lowercase(filename), ".lyx");
85 bool IsSGMLFilename(string const & filename)
87 return suffixIs(lowercase(filename), ".sgml");
91 // Substitutes spaces with underscores in filename (and path)
92 string const MakeLatexName(string const & file)
94 string name = OnlyFilename(file);
95 string const path = OnlyPath(file);
97 for (string::size_type i = 0; i < name.length(); ++i) {
98 name[i] &= 0x7f; // set 8th bit to 0
101 // ok so we scan through the string twice, but who cares.
102 string const keep("abcdefghijklmnopqrstuvwxyz"
103 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
104 "@!\"'()*+,-./0123456789:;<=>?[]`|");
106 string::size_type pos = 0;
107 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
110 return AddName(path, name);
114 // Substitutes spaces with underscores in filename (and path)
115 string const QuoteName(string const & name)
117 return (os::shell() == os::UNIX) ?
123 // Is a file readable ?
124 bool IsFileReadable (string const & path)
127 if (file.isOK() && file.isRegular() && file.readable())
134 // Is a file read_only?
135 // return 1 read-write
137 // -1 error (doesn't exist, no access, anything else)
138 int IsFileWriteable (string const & path)
142 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
144 if (fi.readable()) // read-only
146 return -1; // everything else.
150 //returns true: dir writeable
151 // false: not writeable
152 bool IsDirWriteable (string const & path)
154 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
156 string const tmpfl(lyx::tempName(path, "lyxwritetest"));
166 // Uses a string of paths separated by ";"s to find a file to open.
167 // Can't cope with pathnames with a ';' in them. Returns full path to file.
168 // If path entry begins with $$LyX/, use system_lyxdir
169 // If path entry begins with $$User/, use user_lyxdir
170 // Example: "$$User/doc;$$LyX/doc"
171 string const FileOpenSearch (string const & path, string const & name,
174 string real_file, path_element;
175 bool notfound = true;
176 string tmppath = split(path, path_element, ';');
178 while (notfound && !path_element.empty()) {
179 path_element = os::slashify_path(path_element);
180 if (!suffixIs(path_element, '/'))
182 path_element = subst(path_element, "$$LyX", system_lyxdir);
183 path_element = subst(path_element, "$$User", user_lyxdir);
185 real_file = FileSearch(path_element, name, ext);
187 if (real_file.empty()) {
189 tmppath = split(tmppath, path_element, ';');
190 } while (!tmppath.empty() && path_element.empty());
196 if (ext.empty() && notfound) {
197 real_file = FileOpenSearch(path, name, "exe");
198 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
205 /// Returns a vector of all files in directory dir having extension ext.
206 vector<string> const DirList(string const & dir, string const & ext)
208 // This is a non-error checking C/system implementation
209 string extension(ext);
210 if (!extension.empty() && extension[0] != '.')
211 extension.insert(0, ".");
212 vector<string> dirlist;
213 DIR * dirp = ::opendir(dir.c_str());
216 << "Directory \"" << dir
217 << "\" does not exist to DirList." << endl;
222 while ((dire = ::readdir(dirp))) {
223 string const fil = dire->d_name;
224 if (suffixIs(fil, extension)) {
225 dirlist.push_back(fil);
230 /* I would have prefered to take a vector<string>& as parameter so
231 that we could avoid the copy of the vector when returning.
233 dirlist.swap(argvec);
234 to avoid the copy. (Lgb)
236 /* A C++ implementaion will look like this:
237 string extension(ext);
238 if (extension[0] != '.') extension.insert(0, ".");
239 vector<string> dirlist;
240 directory_iterator dit("dir");
241 while (dit != directory_iterator()) {
242 string fil = dit->filename;
243 if (prefixIs(fil, extension)) {
244 dirlist.push_back(fil);
248 dirlist.swap(argvec);
254 // Returns the real name of file name in directory path, with optional
256 string const FileSearch(string const & path, string const & name,
259 // if `name' is an absolute path, we ignore the setting of `path'
260 // Expand Environmentvariables in 'name'
261 string const tmpname = ReplaceEnvironmentPath(name);
262 string fullname = MakeAbsPath(tmpname, path);
263 // search first without extension, then with it.
264 if (IsFileReadable(fullname))
266 else if (ext.empty())
268 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
271 if (IsFileReadable(fullname))
279 // Search the file name.ext in the subdirectory dir of
281 // 2) build_lyxdir (if not empty)
283 string const LibFileSearch(string const & dir, string const & name,
286 string fullname = FileSearch(AddPath(user_lyxdir, dir), name, ext);
287 if (!fullname.empty())
290 if (!build_lyxdir.empty())
291 fullname = FileSearch(AddPath(build_lyxdir, dir), name, ext);
292 if (!fullname.empty())
295 return FileSearch(AddPath(system_lyxdir, dir), name, ext);
300 i18nLibFileSearch(string const & dir, string const & name,
303 // this comment is from intl/dcigettext.c. We try to mimick this
305 /* The highest priority value is the `LANGUAGE' environment
306 variable. But we don't use the value if the currently
307 selected locale is the C locale. This is a GNU extension. */
309 string const lc_all = GetEnv("LC_ALL");
310 string lang = GetEnv("LANGUAGE");
311 if (lang.empty() || lc_all == "C") {
314 lang = GetEnv("LANG");
318 lang = token(lang, '_', 0);
320 if (lang.empty() || lang == "C")
321 return LibFileSearch(dir, name, ext);
323 string const tmp = LibFileSearch(dir, lang + '_' + name,
328 return LibFileSearch(dir, name, ext);
333 string const LibScriptSearch(string const & command)
336 string args = command;
337 split(args, script, ' ');
338 script = LibFileSearch("scripts", script);
341 else if (args.empty())
344 return script + ' ' + args;
348 string const GetEnv(string const & envname)
350 // f.ex. what about error checking?
351 char const * const ch = getenv(envname.c_str());
352 string const envstr = !ch ? "" : ch;
357 string const GetEnvPath(string const & name)
360 string const pathlist = subst(GetEnv(name), ':', ';');
362 string const pathlist = os::slashify_path(GetEnv(name));
364 return strip(pathlist, ';');
368 bool PutEnv(string const & envstr)
370 // CHECK Look at and fix this.
371 // f.ex. what about error checking?
374 // this leaks, but what can we do about it?
375 // Is doing a getenv() and a free() of the older value
376 // a good idea? (JMarc)
377 // Actually we don't have to leak...calling putenv like this
378 // should be enough: ... and this is obviously not enough if putenv
379 // does not make a copy of the string. It is also not very wise to
380 // put a string on the free store. If we have to leak we should do it
382 char * leaker = new char[envstr.length() + 1];
383 envstr.copy(leaker, envstr.length());
384 leaker[envstr.length()] = '\0';
385 int const retval = lyx::putenv(leaker);
387 // If putenv does not make a copy of the char const * this
388 // is very dangerous. OTOH if it does take a copy this is the
390 // The only implementation of putenv that I have seen does not
391 // allocate memory. _And_ after testing the putenv in glibc it
392 // seems that we need to make a copy of the string contents.
393 // I will enable the above.
394 //int retval = lyx::putenv(envstr.c_str());
398 string const str = envstr.split(varname,'=');
399 int const retval = ::setenv(varname.c_str(), str.c_str(), true);
401 // No environment setting function. Can this happen?
402 int const retval = 1; //return an error condition.
409 bool PutEnvPath(string const & envstr)
411 return PutEnv(envstr);
417 int DeleteAllFilesInDir (string const & path)
419 // I have decided that we will be using parts from the boost
420 // library. Check out http://www.boost.org/
421 // For directory access we will then use the directory_iterator.
422 // Then the code will be something like:
423 // directory_iterator dit(path);
424 // directory_iterator dend;
425 // if (dit == dend) {
426 // Alert::err_alert(_("Error! Cannot open directory:"), path);
429 // for (; dit != dend; ++dit) {
430 // string filename(*dit);
431 // if (filename == "." || filename == "..")
433 // string unlinkpath(AddName(path, filename));
434 // if (lyx::unlink(unlinkpath))
435 // Alert::err_alert(_("Error! Could not remove file:"),
439 DIR * dir = ::opendir(path.c_str());
441 Alert::err_alert (_("Error! Cannot open directory:"), path);
445 int return_value = 0;
446 while ((de = readdir(dir))) {
447 string const temp = de->d_name;
448 if (temp == "." || temp == "..")
450 string const unlinkpath = AddName (path, temp);
452 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
456 FileInfo fi(unlinkpath);
457 if (fi.isOK() && fi.isDir())
458 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
459 deleted &= (lyx::unlink(unlinkpath) == 0);
461 Alert::err_alert(_("Error! Could not remove file:"),
471 string const CreateTmpDir(string const & tempdir, string const & mask)
474 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
475 << "CreateTmpDir: mask=`" << mask << "'" << endl;
477 string const tmpfl(lyx::tempName(tempdir, mask));
478 // lyx::tempName actually creates a file to make sure that it
479 // stays unique. So we have to delete it before we can create
480 // a dir with the same name. Note also that we are not thread
481 // safe because of the gap between unlink and mkdir. (Lgb)
482 lyx::unlink(tmpfl.c_str());
484 if (tmpfl.empty() || lyx::mkdir(tmpfl, 0700)) {
485 Alert::err_alert(_("Error! Couldn't create temporary directory:"),
489 return MakeAbsPath(tmpfl);
493 int DestroyTmpDir(string const & tmpdir, bool Allfiles)
498 if (Allfiles && DeleteAllFilesInDir(tmpdir)) {
501 if (lyx::rmdir(tmpdir)) {
502 Alert::err_alert(_("Error! Couldn't delete temporary directory:"),
512 string const CreateBufferTmpDir(string const & pathfor)
515 static string const tmpdir(pathfor.empty() ? os::getTmpDir() : pathfor);
516 // We are in our own directory. Why bother to mangle name?
517 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
519 string const tmpfl = tmpdir + "/lyx_tmpbuf" + tostr(count++);
520 if (lyx::mkdir(tmpfl, 0777)) {
521 Alert::err_alert(_("Error! Couldn't create temporary directory:"),
529 int DestroyBufferTmpDir(string const & tmpdir)
531 return DestroyTmpDir(tmpdir, true);
535 string const CreateLyXTmpDir(string const & deflt)
537 if ((!deflt.empty()) && (deflt != "/tmp")) {
538 if (lyx::mkdir(deflt, 0777)) {
542 return CreateTmpDir(deflt, "lyx_tmpdir");
549 return CreateTmpDir("/tmp", "lyx_tmpdir");
554 int DestroyLyXTmpDir(string const & tmpdir)
556 return DestroyTmpDir (tmpdir, false); // Why false?
560 // Creates directory. Returns true if succesfull
561 bool createDirectory(string const & path, int permission)
563 string temp(strip(os::slashify_path(path), '/'));
566 Alert::alert(_("Internal error!"),
567 _("Call to createDirectory with invalid name"));
571 if (lyx::mkdir(temp, permission)) {
572 Alert::err_alert (_("Error! Couldn't create directory:"), temp);
579 // Strip filename from path name
580 string const OnlyPath(string const & Filename)
582 // If empty filename, return empty
583 if (Filename.empty()) return Filename;
585 // Find last / or start of filename
586 string::size_type j = Filename.rfind('/');
587 if (j == string::npos)
589 return Filename.substr(0, j + 1);
593 // Convert relative path into absolute path based on a basepath.
594 // If relpath is absolute, just use that.
595 // If basepath is empty, use CWD as base.
596 string const MakeAbsPath(string const & RelPath, string const & BasePath)
598 // checks for already absolute path
599 if (os::is_absolute_path(RelPath))
602 // Copies given paths
603 string TempRel(os::slashify_path(RelPath));
604 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
605 TempRel = subst(TempRel, "//", "/");
609 if (os::is_absolute_path(BasePath))
612 TempBase = AddPath(lyx::getcwd(), BasePath);
614 // Handle /./ at the end of the path
615 while (suffixIs(TempBase, "/./"))
616 TempBase.erase(TempBase.length() - 2);
618 // processes relative path
619 string RTemp(TempRel);
622 while (!RTemp.empty()) {
624 RTemp = split(RTemp, Temp, '/');
626 if (Temp == ".") continue;
628 // Remove one level of TempBase
629 string::difference_type i = TempBase.length() - 2;
632 while (i > 0 && TempBase[i] != '/') --i;
636 while (i > 2 && TempBase[i] != '/') --i;
639 TempBase.erase(i, string::npos);
642 } else if (Temp.empty() && !RTemp.empty()) {
643 TempBase = os::current_root() + RTemp;
646 // Add this piece to TempBase
647 if (!suffixIs(TempBase, '/'))
653 // returns absolute path
654 return os::slashify_path(TempBase);
658 // Correctly append filename to the pathname.
659 // If pathname is '.', then don't use pathname.
660 // Chops any path of filename.
661 string const AddName(string const & path, string const & fname)
664 string const basename(OnlyFilename(fname));
668 if (path != "." && path != "./" && !path.empty()) {
669 buf = os::slashify_path(path);
670 if (!suffixIs(path, '/'))
674 return buf + basename;
678 // Strips path from filename
679 string const OnlyFilename(string const & fname)
684 string::size_type j = fname.rfind('/');
685 if (j == string::npos) // no '/' in fname
689 return fname.substr(j + 1);
693 /// Returns true is path is absolute
694 bool AbsolutePath(string const & path)
696 return os::is_absolute_path(path);
701 // Create absolute path. If impossible, don't do anything
702 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
703 string const ExpandPath(string const & path)
705 // checks for already absolute path
706 string RTemp(ReplaceEnvironmentPath(path));
707 if (os::is_absolute_path(RTemp))
711 string const copy(RTemp);
714 RTemp = split(RTemp, Temp, '/');
717 return lyx::getcwd() /*GetCWD()*/ + '/' + RTemp;
720 return GetEnvPath("HOME") + '/' + RTemp;
723 return MakeAbsPath(copy);
725 // Don't know how to handle this
731 // Constracts path/../path
732 // Can't handle "../../" or "/../" (Asger)
733 string const NormalizePath(string const & path)
739 if (os::is_absolute_path(path))
742 // Make implicit current directory explicit
745 while (!RTemp.empty()) {
747 RTemp = split(RTemp, Temp, '/');
751 } else if (Temp == "..") {
752 // Remove one level of TempBase
753 string::difference_type i = TempBase.length() - 2;
754 while (i > 0 && TempBase[i] != '/')
756 if (i >= 0 && TempBase[i] == '/')
757 TempBase.erase(i + 1, string::npos);
761 TempBase += Temp + '/';
765 // returns absolute path
770 string const GetFileContents(string const & fname)
772 FileInfo finfo(fname);
774 ifstream ifs(fname.c_str());
779 return ofs.str().c_str();
782 lyxerr << "LyX was not able to read file '" << fname << "'" << endl;
788 // Search ${...} as Variable-Name inside the string and replace it with
789 // the denoted environmentvariable
790 // Allow Variables according to
791 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
794 string const ReplaceEnvironmentPath(string const & path)
797 // CompareChar: Environmentvariables starts with this character
798 // PathChar: Next path component start with this character
799 // while CompareChar found do:
800 // Split String with PathChar
801 // Search Environmentvariable
802 // if found: Replace Strings
804 char const CompareChar = '$';
805 char const FirstChar = '{';
806 char const EndChar = '}';
807 char const UnderscoreChar = '_';
808 string EndString; EndString += EndChar;
809 string FirstString; FirstString += FirstChar;
810 string CompareString; CompareString += CompareChar;
811 string const RegExp("*}*"); // Exist EndChar inside a String?
813 // first: Search for a '$' - Sign.
815 string result1; //(copy); // for split-calls
816 string result0 = split(path, result1, CompareChar);
817 while (!result0.empty()) {
818 string copy1(result0); // contains String after $
820 // Check, if there is an EndChar inside original String.
822 if (!regexMatch(copy1, RegExp)) {
823 // No EndChar inside. So we are finished
824 result1 += CompareString + result0;
830 string res0 = split(copy1, res1, EndChar);
831 // Now res1 holds the environmentvariable
832 // First, check, if Contents is ok.
833 if (res1.empty()) { // No environmentvariable. Continue Loop.
834 result1 += CompareString + FirstString;
838 // check contents of res1
839 char const * res1_contents = res1.c_str();
840 if (*res1_contents != FirstChar) {
841 // Again No Environmentvariable
842 result1 += CompareString;
846 // Check for variable names
847 // Situation ${} is detected as "No Environmentvariable"
848 char const * cp1 = res1_contents + 1;
849 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
851 while (*cp1 && result) {
852 result = isalnum(*cp1) ||
853 (*cp1 == UnderscoreChar);
858 // no correct variable name
859 result1 += CompareString + res1 + EndString;
860 result0 = split(res0, res1, CompareChar);
865 string env(GetEnv(res1_contents + 1));
867 // Congratulations. Environmentvariable found
870 result1 += CompareString + res1 + EndString;
873 result0 = split(res0, res1, CompareChar);
877 } // ReplaceEnvironmentPath
880 // Make relative path out of two absolute paths
881 string const MakeRelPath(string const & abspath, string const & basepath)
882 // Makes relative path out of absolute path. If it is deeper than basepath,
883 // it's easy. If basepath and abspath share something (they are all deeper
884 // than some directory), it'll be rendered using ..'s. If they are completely
885 // different, then the absolute path will be used as relative path.
887 string::size_type const abslen = abspath.length();
888 string::size_type const baselen = basepath.length();
890 string::size_type i = os::common_path(abspath, basepath);
893 // actually no match - cannot make it relative
897 // Count how many dirs there are in basepath above match
898 // and append as many '..''s into relpath
900 string::size_type j = i;
901 while (j < baselen) {
902 if (basepath[j] == '/') {
903 if (j + 1 == baselen)
910 // Append relative stuff from common directory to abspath
911 if (abspath[i] == '/')
913 for (; i < abslen; ++i)
916 if (suffixIs(buf, '/'))
917 buf.erase(buf.length() - 1);
918 // Substitute empty with .
925 // Append sub-directory(ies) to a path in an intelligent way
926 string const AddPath(string const & path, string const & path_2)
929 string const path2 = os::slashify_path(path_2);
931 if (!path.empty() && path != "." && path != "./") {
932 buf = os::slashify_path(path);
933 if (path[path.length() - 1] != '/')
937 if (!path2.empty()) {
938 string::size_type const p2start = path2.find_first_not_of('/');
939 string::size_type const p2end = path2.find_last_not_of('/');
940 string const tmp = path2.substr(p2start, p2end - p2start + 1);
948 Change extension of oldname to extension.
949 Strips path off if no_path == true.
950 If no extension on oldname, just appends.
952 string const ChangeExtension(string const & oldname, string const & extension)
954 string::size_type const last_slash = oldname.rfind('/');
955 string::size_type last_dot = oldname.rfind('.');
956 if (last_dot < last_slash && last_slash != string::npos)
957 last_dot = string::npos;
960 // Make sure the extension starts with a dot
961 if (!extension.empty() && extension[0] != '.')
962 ext= "." + extension;
966 return os::slashify_path(oldname.substr(0, last_dot) + ext);
970 /// Return the extension of the file (not including the .)
971 string const GetExtension(string const & name)
973 string::size_type const last_slash = name.rfind('/');
974 string::size_type const last_dot = name.rfind('.');
975 if (last_dot != string::npos &&
976 (last_slash == string::npos || last_dot > last_slash))
977 return name.substr(last_dot + 1,
978 name.length() - (last_dot + 1));
983 // the different filetypes and what they contain in one of the first lines
984 // (dots are any characters). (Herbert 20020131)
987 // EPS %!PS-Adobe-3.0 EPSF...
993 // PBM P1... or P4 (B/W)
994 // PGM P2... or P5 (Grayscale)
995 // PPM P3... or P6 (color)
996 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
997 // SGI \001\332... (decimal 474)
999 // TIFF II... or MM...
1000 // XBM ..._bits[]...
1001 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
1002 // ...static char *...
1003 // XWD \000\000\000\151 (0x00006900) decimal 105
1005 // GZIP \037\213\010\010... http://www.ietf.org/rfc/rfc1952.txt
1006 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
1007 // Z \037\177 UNIX compress
1009 /// return the "extension" which belongs to the contents.
1010 /// for no knowing contents return the extension. Without
1011 /// an extension and unknown contents we return "user"
1012 string const getExtFromContents(string const & filename)
1015 if (filename.empty() || !IsFileReadable(filename))
1018 ifstream ifs(filename.c_str());
1020 // Couldn't open file...
1024 string const gzipStamp = "\037\213\010\010";
1027 string const zipStamp = "PK";
1030 string const compressStamp = "\037\177";
1032 // Maximum strings to read
1033 int const max_count = 50;
1037 bool firstLine = true;
1038 while ((count++ < max_count) && format.empty()) {
1040 lyxerr[Debug::GRAPHICS]
1041 << "filetools(getExtFromContents)\n"
1042 << "\tFile type not recognised before EOF!"
1049 lyxerr[Debug::GRAPHICS] << "Scanstring: " << str.substr(0,60)
1052 string const stamp = str.substr(0,2);
1053 if (firstLine && str.size() >= 2) {
1054 // at first we check for a zipped file, because this
1055 // information is saved in the first bytes of the file!
1056 // also some graphic formats which save the information
1057 // in the first line, too.
1058 if (prefixIs(str, gzipStamp)) {
1061 } else if (stamp == zipStamp) {
1064 } else if (stamp == compressStamp) {
1065 format = "compress";
1067 // the graphics part
1068 } else if (stamp == "BM") {
1071 } else if (stamp == "\001\332") {
1075 // Don't need to use str.at(0), str.at(1) because
1076 // we already know that str.size() >= 2
1077 } else if (str[0] == 'P') {
1093 } else if ((stamp == "II") || (stamp == "MM")) {
1096 } else if (prefixIs(str,"%TGIF")) {
1099 } else if (prefixIs(str,"GIF")) {
1102 } else if (str.size() > 3) {
1103 int const c = ((str[0] << 24) & (str[1] << 16) &
1104 (str[2] << 8) & str[3]);
1113 if (!format.empty())
1115 else if (contains(str,"EPSF"))
1116 // dummy, if we have wrong file description like
1117 // %!PS-Adobe-2.0EPSF"
1120 else if (contains(str,"Grace"))
1123 else if (contains(str,"JFIF"))
1126 else if (contains(str,"%PDF"))
1129 else if (contains(str,"PNG"))
1132 else if (contains(str,"%!PS-Adobe")) {
1135 if (contains(str,"EPSF"))
1141 else if (contains(str,"_bits[]"))
1144 else if (contains(str,"XPM") || contains(str, "static char *"))
1147 else if (contains(str,"BITPIX"))
1151 if (!format.empty()) {
1152 lyxerr[Debug::GRAPHICS]
1153 << "Recognised Fileformat: " << format << endl;
1157 string const ext(GetExtension(filename));
1158 lyxerr[Debug::GRAPHICS]
1159 << "filetools(getExtFromContents)\n"
1160 << "\tCouldn't find a known Type!\n";
1162 lyxerr[Debug::GRAPHICS]
1163 << "\twill take the file extension -> "
1167 lyxerr[Debug::GRAPHICS]
1168 << "\twill use ext or a \"user\" defined format" << endl;
1174 /// check for zipped file
1175 bool zippedFile(string const & name)
1177 string const type = getExtFromContents(name);
1178 if (contains("gzip zip compress", type) && !type.empty())
1184 string const unzipFile(string const & zipped_file)
1186 string const file = ChangeExtension(zipped_file, string());
1187 string const tempfile = lyx::tempName(string(), file);
1189 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1191 one.startscript(Systemcall::Wait, command);
1192 // test that command was executed successfully (anon)
1193 // yes, please do. (Lgb)
1198 // Creates a nice compact path for displaying
1200 MakeDisplayPath (string const & path, unsigned int threshold)
1202 string::size_type const l1 = path.length();
1204 // First, we try a relative path compared to home
1205 string const home(GetEnvPath("HOME"));
1206 string relhome = MakeRelPath(path, home);
1208 string::size_type l2 = relhome.length();
1212 // If we backup from home or don't have a relative path,
1213 // this try is no good
1214 if (prefixIs(relhome, "../") || os::is_absolute_path(relhome)) {
1215 // relative path was no good, just use the original path
1222 // Is the path too long?
1223 if (l2 > threshold) {
1229 while (relhome.length() > threshold)
1230 relhome = split(relhome, temp, '/');
1232 // Did we shortend everything away?
1233 if (relhome.empty()) {
1234 // Yes, filename in itself is too long.
1235 // Pick the start and the end of the filename.
1236 relhome = OnlyFilename(path);
1237 string const head = relhome.substr(0, threshold/2 - 3);
1239 l2 = relhome.length();
1241 relhome.substr(l2 - threshold/2 - 2, l2 - 1);
1242 relhome = head + "..." + tail;
1245 return prefix + relhome;
1249 bool LyXReadLink(string const & File, string & Link)
1251 char LinkBuffer[512];
1252 // Should be PATH_MAX but that needs autconf support
1253 int const nRead = ::readlink(File.c_str(),
1254 LinkBuffer, sizeof(LinkBuffer) - 1);
1257 LinkBuffer[nRead] = '\0'; // terminator
1265 typedef pair<int, string> cmdret;
1267 cmdret const do_popen(string const & cmd)
1269 // One question is if we should use popen or
1270 // create our own popen based on fork, exec, pipe
1271 // of course the best would be to have a
1272 // pstream (process stream), with the
1273 // variants ipstream, opstream
1275 FILE * inf = ::popen(cmd.c_str(), os::read_mode());
1277 // (Claus Hentschel) Check if popen was succesful ;-)
1279 return make_pair(-1, string());
1284 ret += static_cast<char>(c);
1287 int const pret = pclose(inf);
1288 return make_pair(pret, ret);
1294 string const findtexfile(string const & fil, string const & /*format*/)
1296 /* There is no problem to extend this function too use other
1297 methods to look for files. It could be setup to look
1298 in environment paths and also if wanted as a last resort
1299 to a recursive find. One of the easier extensions would
1300 perhaps be to use the LyX file lookup methods. But! I am
1301 going to implement this until I see some demand for it.
1305 // If the file can be found directly, we just return a
1306 // absolute path version of it.
1307 if (FileInfo(fil).exist())
1308 return MakeAbsPath(fil);
1310 // No we try to find it using kpsewhich.
1311 // It seems from the kpsewhich manual page that it is safe to use
1312 // kpsewhich without --format: "When the --format option is not
1313 // given, the search path used when looking for a file is inferred
1314 // from the name given, by looking for a known extension. If no
1315 // known extension is found, the search path for TeX source files
1317 // However, we want to take advantage of the format sine almost all
1318 // the different formats has environment variables that can be used
1319 // to controll which paths to search. f.ex. bib looks in
1320 // BIBINPUTS and TEXBIB. Small list follows:
1321 // bib - BIBINPUTS, TEXBIB
1323 // graphic/figure - TEXPICTS, TEXINPUTS
1324 // ist - TEXINDEXSTYLE, INDEXSTYLE
1325 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1327 // tfm - TFMFONTS, TEXFONTS
1328 // This means that to use kpsewhich in the best possible way we
1329 // should help it by setting additional path in the approp. envir.var.
1330 string const kpsecmd = "kpsewhich " + fil;
1332 cmdret const c = do_popen(kpsecmd);
1334 lyxerr[Debug::LATEX] << "kpse status = " << c.first << "\n"
1335 << "kpse result = `" << strip(c.second, '\n')
1338 return os::internal_path(strip(strip(c.second, '\n'), '\r'));
1344 void removeAutosaveFile(string const & filename)
1346 string a = OnlyPath(filename);
1348 a += OnlyFilename(filename);
1350 FileInfo const fileinfo(a);
1351 if (fileinfo.exist()) {
1352 if (lyx::unlink(a) != 0) {
1353 Alert::err_alert(_("Could not delete auto-save file!"), a);
1359 string const readBB_from_PSFile(string const & file)
1361 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1362 // It seems that every command in the header has an own line,
1363 // getline() should work for all files.
1364 // On the other hand some plot programs write the bb at the
1365 // end of the file. Than we have in the header:
1366 // %%BoundingBox: (atend)
1367 // In this case we must check the end.
1368 string const file_ = zippedFile(file) ?
1369 string(unzipFile(file)) : string(file);
1370 string const format = getExtFromContents(file_);
1371 if (format != "eps" && format != "ps")
1374 std::ifstream is(file_.c_str());
1378 if (contains(s,"%%BoundingBox:") && !contains(s,"atend"))
1379 return (frontStrip(s.substr(14)));