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 "frontends/Alert.h"
41 #include "support/path.h" // I know it's OS/2 specific (SMiyata)
46 // Which part of this is still necessary? (JMarc).
49 # define NAMLEN(dirent) strlen((dirent)->d_name)
51 # define dirent direct
52 # define NAMLEN(dirent) (dirent)->d_namlen
54 # include <sys/ndir.h>
71 extern string system_lyxdir;
72 extern string build_lyxdir;
73 extern string user_lyxdir;
74 extern string system_tempdir;
75 extern string system_packageList;
78 bool IsLyXFilename(string const & filename)
80 return suffixIs(lowercase(filename), ".lyx");
84 bool IsSGMLFilename(string const & filename)
86 return suffixIs(lowercase(filename), ".sgml");
90 // Substitutes spaces with underscores in filename (and path)
91 string const MakeLatexName(string const & file)
93 string name = OnlyFilename(file);
94 string const path = OnlyPath(file);
96 for (string::size_type i = 0; i < name.length(); ++i) {
97 name[i] &= 0x7f; // set 8th bit to 0
100 // ok so we scan through the string twice, but who cares.
101 string const keep("abcdefghijklmnopqrstuvwxyz"
102 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
103 "@!\"'()*+,-./0123456789:;<=>?[]`|");
105 string::size_type pos = 0;
106 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
109 return AddName(path, name);
113 // Substitutes spaces with underscores in filename (and path)
114 string const QuoteName(string const & name)
116 return (os::shell() == os::UNIX) ?
122 // Is a file readable ?
123 bool IsFileReadable (string const & path)
126 if (file.isOK() && file.isRegular() && file.readable())
133 // Is a file read_only?
134 // return 1 read-write
136 // -1 error (doesn't exist, no access, anything else)
137 int IsFileWriteable (string const & path)
141 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
143 if (fi.readable()) // read-only
145 return -1; // everything else.
149 //returns true: dir writeable
150 // false: not writeable
151 bool IsDirWriteable (string const & path)
153 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
155 string const tmpfl(lyx::tempName(path, "lyxwritetest"));
165 // Uses a string of paths separated by ";"s to find a file to open.
166 // Can't cope with pathnames with a ';' in them. Returns full path to file.
167 // If path entry begins with $$LyX/, use system_lyxdir
168 // If path entry begins with $$User/, use user_lyxdir
169 // Example: "$$User/doc;$$LyX/doc"
170 string const FileOpenSearch (string const & path, string const & name,
173 string real_file, path_element;
174 bool notfound = true;
175 string tmppath = split(path, path_element, ';');
177 while (notfound && !path_element.empty()) {
178 path_element = os::slashify_path(path_element);
179 if (!suffixIs(path_element, '/'))
181 path_element = subst(path_element, "$$LyX", system_lyxdir);
182 path_element = subst(path_element, "$$User", user_lyxdir);
184 real_file = FileSearch(path_element, name, ext);
186 if (real_file.empty()) {
188 tmppath = split(tmppath, path_element, ';');
189 } while (!tmppath.empty() && path_element.empty());
195 if (ext.empty() && notfound) {
196 real_file = FileOpenSearch(path, name, "exe");
197 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
204 /// Returns a vector of all files in directory dir having extension ext.
205 vector<string> const DirList(string const & dir, string const & ext)
207 // This is a non-error checking C/system implementation
208 string extension(ext);
209 if (!extension.empty() && extension[0] != '.')
210 extension.insert(0, ".");
211 vector<string> dirlist;
212 DIR * dirp = ::opendir(dir.c_str());
215 << "Directory \"" << dir
216 << "\" does not exist to DirList." << endl;
221 while ((dire = ::readdir(dirp))) {
222 string const fil = dire->d_name;
223 if (suffixIs(fil, extension)) {
224 dirlist.push_back(fil);
229 /* I would have prefered to take a vector<string>& as parameter so
230 that we could avoid the copy of the vector when returning.
232 dirlist.swap(argvec);
233 to avoid the copy. (Lgb)
235 /* A C++ implementaion will look like this:
236 string extension(ext);
237 if (extension[0] != '.') extension.insert(0, ".");
238 vector<string> dirlist;
239 directory_iterator dit("dir");
240 while (dit != directory_iterator()) {
241 string fil = dit->filename;
242 if (prefixIs(fil, extension)) {
243 dirlist.push_back(fil);
247 dirlist.swap(argvec);
253 // Returns the real name of file name in directory path, with optional
255 string const FileSearch(string const & path, string const & name,
258 // if `name' is an absolute path, we ignore the setting of `path'
259 // Expand Environmentvariables in 'name'
260 string const tmpname = ReplaceEnvironmentPath(name);
261 string fullname = MakeAbsPath(tmpname, path);
262 // search first without extension, then with it.
263 if (IsFileReadable(fullname))
265 else if (ext.empty())
267 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
270 if (IsFileReadable(fullname))
278 // Search the file name.ext in the subdirectory dir of
280 // 2) build_lyxdir (if not empty)
282 string const LibFileSearch(string const & dir, string const & name,
285 string fullname = FileSearch(AddPath(user_lyxdir, dir), name, ext);
286 if (!fullname.empty())
289 if (!build_lyxdir.empty())
290 fullname = FileSearch(AddPath(build_lyxdir, dir), name, ext);
291 if (!fullname.empty())
294 return FileSearch(AddPath(system_lyxdir, dir), name, ext);
299 i18nLibFileSearch(string const & dir, string const & name,
302 // this comment is from intl/dcigettext.c. We try to mimick this
304 /* The highest priority value is the `LANGUAGE' environment
305 variable. But we don't use the value if the currently
306 selected locale is the C locale. This is a GNU extension. */
308 string const lc_all = GetEnv("LC_ALL");
309 string lang = GetEnv("LANGUAGE");
310 if (lang.empty() || lc_all == "C") {
313 lang = GetEnv("LANG");
317 lang = token(lang, '_', 0);
319 if (lang.empty() || lang == "C")
320 return LibFileSearch(dir, name, ext);
322 string const tmp = LibFileSearch(dir, lang + '_' + name,
327 return LibFileSearch(dir, name, ext);
332 string const LibScriptSearch(string const & command)
335 string args = command;
336 split(args, script, ' ');
337 script = LibFileSearch("scripts", script);
340 else if (args.empty())
343 return script + ' ' + args;
347 string const GetEnv(string const & envname)
349 // f.ex. what about error checking?
350 char const * const ch = getenv(envname.c_str());
351 string const envstr = !ch ? "" : ch;
356 string const GetEnvPath(string const & name)
359 string const pathlist = subst(GetEnv(name), ':', ';');
361 string const pathlist = os::slashify_path(GetEnv(name));
363 return strip(pathlist, ';');
367 bool PutEnv(string const & envstr)
369 // CHECK Look at and fix this.
370 // f.ex. what about error checking?
373 // this leaks, but what can we do about it?
374 // Is doing a getenv() and a free() of the older value
375 // a good idea? (JMarc)
376 // Actually we don't have to leak...calling putenv like this
377 // should be enough: ... and this is obviously not enough if putenv
378 // does not make a copy of the string. It is also not very wise to
379 // put a string on the free store. If we have to leak we should do it
381 char * leaker = new char[envstr.length() + 1];
382 envstr.copy(leaker, envstr.length());
383 leaker[envstr.length()] = '\0';
384 int const retval = lyx::putenv(leaker);
386 // If putenv does not make a copy of the char const * this
387 // is very dangerous. OTOH if it does take a copy this is the
389 // The only implementation of putenv that I have seen does not
390 // allocate memory. _And_ after testing the putenv in glibc it
391 // seems that we need to make a copy of the string contents.
392 // I will enable the above.
393 //int retval = lyx::putenv(envstr.c_str());
397 string const str = envstr.split(varname,'=');
398 int const retval = ::setenv(varname.c_str(), str.c_str(), true);
400 // No environment setting function. Can this happen?
401 int const retval = 1; //return an error condition.
408 bool PutEnvPath(string const & envstr)
410 return PutEnv(envstr);
416 int DeleteAllFilesInDir (string const & path)
418 // I have decided that we will be using parts from the boost
419 // library. Check out http://www.boost.org/
420 // For directory access we will then use the directory_iterator.
421 // Then the code will be something like:
422 // directory_iterator dit(path);
423 // directory_iterator dend;
424 // if (dit == dend) {
425 // Alert::err_alert(_("Error! Cannot open directory:"), path);
428 // for (; dit != dend; ++dit) {
429 // string filename(*dit);
430 // if (filename == "." || filename == "..")
432 // string unlinkpath(AddName(path, filename));
433 // if (lyx::unlink(unlinkpath))
434 // Alert::err_alert(_("Error! Could not remove file:"),
438 DIR * dir = ::opendir(path.c_str());
440 Alert::err_alert (_("Error! Cannot open directory:"), path);
444 int return_value = 0;
445 while ((de = readdir(dir))) {
446 string const temp = de->d_name;
447 if (temp == "." || temp == "..")
449 string const unlinkpath = AddName (path, temp);
451 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
455 FileInfo fi(unlinkpath);
456 if (fi.isOK() && fi.isDir())
457 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
458 deleted &= (lyx::unlink(unlinkpath) == 0);
460 Alert::err_alert(_("Error! Could not remove file:"),
470 string const CreateTmpDir(string const & tempdir, string const & mask)
473 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
474 << "CreateTmpDir: mask=`" << mask << "'" << endl;
476 string const tmpfl(lyx::tempName(tempdir, mask));
477 // lyx::tempName actually creates a file to make sure that it
478 // stays unique. So we have to delete it before we can create
479 // a dir with the same name. Note also that we are not thread
480 // safe because of the gap between unlink and mkdir. (Lgb)
481 lyx::unlink(tmpfl.c_str());
483 if (tmpfl.empty() || lyx::mkdir(tmpfl, 0700)) {
484 Alert::err_alert(_("Error! Couldn't create temporary directory:"),
488 return MakeAbsPath(tmpfl);
492 int DestroyTmpDir(string const & tmpdir, bool Allfiles)
497 if (Allfiles && DeleteAllFilesInDir(tmpdir)) {
500 if (lyx::rmdir(tmpdir)) {
501 Alert::err_alert(_("Error! Couldn't delete temporary directory:"),
511 string const CreateBufferTmpDir(string const & pathfor)
514 static string const tmpdir(pathfor.empty() ? os::getTmpDir() : pathfor);
515 // We are in our own directory. Why bother to mangle name?
516 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
518 string const tmpfl = tmpdir + "/lyx_tmpbuf" + tostr(count++);
519 if (lyx::mkdir(tmpfl, 0777)) {
520 Alert::err_alert(_("Error! Couldn't create temporary directory:"),
528 int DestroyBufferTmpDir(string const & tmpdir)
530 return DestroyTmpDir(tmpdir, true);
534 string const CreateLyXTmpDir(string const & deflt)
536 if ((!deflt.empty()) && (deflt != "/tmp")) {
537 if (lyx::mkdir(deflt, 0777)) {
541 return CreateTmpDir(deflt, "lyx_tmpdir");
548 return CreateTmpDir("/tmp", "lyx_tmpdir");
553 int DestroyLyXTmpDir(string const & tmpdir)
555 return DestroyTmpDir (tmpdir, false); // Why false?
559 // Creates directory. Returns true if succesfull
560 bool createDirectory(string const & path, int permission)
562 string temp(strip(os::slashify_path(path), '/'));
565 Alert::alert(_("Internal error!"),
566 _("Call to createDirectory with invalid name"));
570 if (lyx::mkdir(temp, permission)) {
571 Alert::err_alert (_("Error! Couldn't create directory:"), temp);
578 // Strip filename from path name
579 string const OnlyPath(string const & Filename)
581 // If empty filename, return empty
582 if (Filename.empty()) return Filename;
584 // Find last / or start of filename
585 string::size_type j = Filename.rfind('/');
586 if (j == string::npos)
588 return Filename.substr(0, j + 1);
592 // Convert relative path into absolute path based on a basepath.
593 // If relpath is absolute, just use that.
594 // If basepath is empty, use CWD as base.
595 string const MakeAbsPath(string const & RelPath, string const & BasePath)
597 // checks for already absolute path
598 if (os::is_absolute_path(RelPath))
601 // Copies given paths
602 string TempRel(os::slashify_path(RelPath));
603 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
604 TempRel = subst(TempRel, "//", "/");
608 if (os::is_absolute_path(BasePath))
611 TempBase = AddPath(lyx::getcwd(), BasePath);
613 // Handle /./ at the end of the path
614 while (suffixIs(TempBase, "/./"))
615 TempBase.erase(TempBase.length() - 2);
617 // processes relative path
618 string RTemp(TempRel);
621 while (!RTemp.empty()) {
623 RTemp = split(RTemp, Temp, '/');
625 if (Temp == ".") continue;
627 // Remove one level of TempBase
628 string::difference_type i = TempBase.length() - 2;
631 while (i > 0 && TempBase[i] != '/') --i;
635 while (i > 2 && TempBase[i] != '/') --i;
638 TempBase.erase(i, string::npos);
641 } else if (Temp.empty() && !RTemp.empty()) {
642 TempBase = os::current_root() + RTemp;
645 // Add this piece to TempBase
646 if (!suffixIs(TempBase, '/'))
652 // returns absolute path
653 return os::slashify_path(TempBase);
657 // Correctly append filename to the pathname.
658 // If pathname is '.', then don't use pathname.
659 // Chops any path of filename.
660 string const AddName(string const & path, string const & fname)
663 string const basename(OnlyFilename(fname));
667 if (path != "." && path != "./" && !path.empty()) {
668 buf = os::slashify_path(path);
669 if (!suffixIs(path, '/'))
673 return buf + basename;
677 // Strips path from filename
678 string const OnlyFilename(string const & fname)
683 string::size_type j = fname.rfind('/');
684 if (j == string::npos) // no '/' in fname
688 return fname.substr(j + 1);
692 /// Returns true is path is absolute
693 bool AbsolutePath(string const & path)
695 return os::is_absolute_path(path);
700 // Create absolute path. If impossible, don't do anything
701 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
702 string const ExpandPath(string const & path)
704 // checks for already absolute path
705 string RTemp(ReplaceEnvironmentPath(path));
706 if (os::is_absolute_path(RTemp))
710 string const copy(RTemp);
713 RTemp = split(RTemp, Temp, '/');
716 return lyx::getcwd() /*GetCWD()*/ + '/' + RTemp;
719 return GetEnvPath("HOME") + '/' + RTemp;
722 return MakeAbsPath(copy);
724 // Don't know how to handle this
730 // Constracts path/../path
731 // Can't handle "../../" or "/../" (Asger)
732 string const NormalizePath(string const & path)
738 if (os::is_absolute_path(path))
741 // Make implicit current directory explicit
744 while (!RTemp.empty()) {
746 RTemp = split(RTemp, Temp, '/');
750 } else if (Temp == "..") {
751 // Remove one level of TempBase
752 string::difference_type i = TempBase.length() - 2;
753 while (i > 0 && TempBase[i] != '/')
755 if (i >= 0 && TempBase[i] == '/')
756 TempBase.erase(i + 1, string::npos);
760 TempBase += Temp + '/';
764 // returns absolute path
769 string const GetFileContents(string const & fname)
771 FileInfo finfo(fname);
773 ifstream ifs(fname.c_str());
778 return ofs.str().c_str();
781 lyxerr << "LyX was not able to read file '" << fname << "'" << endl;
787 // Search ${...} as Variable-Name inside the string and replace it with
788 // the denoted environmentvariable
789 // Allow Variables according to
790 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
793 string const ReplaceEnvironmentPath(string const & path)
796 // CompareChar: Environmentvariables starts with this character
797 // PathChar: Next path component start with this character
798 // while CompareChar found do:
799 // Split String with PathChar
800 // Search Environmentvariable
801 // if found: Replace Strings
803 char const CompareChar = '$';
804 char const FirstChar = '{';
805 char const EndChar = '}';
806 char const UnderscoreChar = '_';
807 string EndString; EndString += EndChar;
808 string FirstString; FirstString += FirstChar;
809 string CompareString; CompareString += CompareChar;
810 string const RegExp("*}*"); // Exist EndChar inside a String?
812 // first: Search for a '$' - Sign.
814 string result1; //(copy); // for split-calls
815 string result0 = split(path, result1, CompareChar);
816 while (!result0.empty()) {
817 string copy1(result0); // contains String after $
819 // Check, if there is an EndChar inside original String.
821 if (!regexMatch(copy1, RegExp)) {
822 // No EndChar inside. So we are finished
823 result1 += CompareString + result0;
829 string res0 = split(copy1, res1, EndChar);
830 // Now res1 holds the environmentvariable
831 // First, check, if Contents is ok.
832 if (res1.empty()) { // No environmentvariable. Continue Loop.
833 result1 += CompareString + FirstString;
837 // check contents of res1
838 char const * res1_contents = res1.c_str();
839 if (*res1_contents != FirstChar) {
840 // Again No Environmentvariable
841 result1 += CompareString;
845 // Check for variable names
846 // Situation ${} is detected as "No Environmentvariable"
847 char const * cp1 = res1_contents + 1;
848 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
850 while (*cp1 && result) {
851 result = isalnum(*cp1) ||
852 (*cp1 == UnderscoreChar);
857 // no correct variable name
858 result1 += CompareString + res1 + EndString;
859 result0 = split(res0, res1, CompareChar);
864 string env(GetEnv(res1_contents + 1));
866 // Congratulations. Environmentvariable found
869 result1 += CompareString + res1 + EndString;
872 result0 = split(res0, res1, CompareChar);
876 } // ReplaceEnvironmentPath
879 // Make relative path out of two absolute paths
880 string const MakeRelPath(string const & abspath, string const & basepath)
881 // Makes relative path out of absolute path. If it is deeper than basepath,
882 // it's easy. If basepath and abspath share something (they are all deeper
883 // than some directory), it'll be rendered using ..'s. If they are completely
884 // different, then the absolute path will be used as relative path.
886 string::size_type const abslen = abspath.length();
887 string::size_type const baselen = basepath.length();
889 string::size_type i = os::common_path(abspath, basepath);
892 // actually no match - cannot make it relative
896 // Count how many dirs there are in basepath above match
897 // and append as many '..''s into relpath
899 string::size_type j = i;
900 while (j < baselen) {
901 if (basepath[j] == '/') {
902 if (j + 1 == baselen)
909 // Append relative stuff from common directory to abspath
910 if (abspath[i] == '/')
912 for (; i < abslen; ++i)
915 if (suffixIs(buf, '/'))
916 buf.erase(buf.length() - 1);
917 // Substitute empty with .
924 // Append sub-directory(ies) to a path in an intelligent way
925 string const AddPath(string const & path, string const & path_2)
928 string const path2 = os::slashify_path(path_2);
930 if (!path.empty() && path != "." && path != "./") {
931 buf = os::slashify_path(path);
932 if (path[path.length() - 1] != '/')
936 if (!path2.empty()) {
937 string::size_type const p2start = path2.find_first_not_of('/');
938 string::size_type const p2end = path2.find_last_not_of('/');
939 string const tmp = path2.substr(p2start, p2end - p2start + 1);
947 Change extension of oldname to extension.
948 Strips path off if no_path == true.
949 If no extension on oldname, just appends.
951 string const ChangeExtension(string const & oldname, string const & extension)
953 string::size_type const last_slash = oldname.rfind('/');
954 string::size_type last_dot = oldname.rfind('.');
955 if (last_dot < last_slash && last_slash != string::npos)
956 last_dot = string::npos;
959 // Make sure the extension starts with a dot
960 if (!extension.empty() && extension[0] != '.')
961 ext= "." + extension;
965 return os::slashify_path(oldname.substr(0, last_dot) + ext);
969 /// Return the extension of the file (not including the .)
970 string const GetExtension(string const & name)
972 string::size_type const last_slash = name.rfind('/');
973 string::size_type const last_dot = name.rfind('.');
974 if (last_dot != string::npos &&
975 (last_slash == string::npos || last_dot > last_slash))
976 return name.substr(last_dot + 1,
977 name.length() - (last_dot + 1));
982 // the different filetypes and what they contain in one of the first lines
983 // (dots are any characters). (Herbert 20020131)
986 // EPS %!PS-Adobe-3.0 EPSF...
992 // PBM P1... or P4 (B/W)
993 // PGM P2... or P5 (Grayscale)
994 // PPM P3... or P6 (color)
995 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
996 // SGI \001\332... (decimal 474)
998 // TIFF II... or MM...
1000 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
1001 // ...static char *...
1002 // XWD \000\000\000\151 (0x00006900) decimal 105
1004 // GZIP \037\213\010\010... http://www.ietf.org/rfc/rfc1952.txt
1005 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
1006 // Z \037\177 UNIX compress
1008 /// return the "extension" which belongs to the contents.
1009 /// for no knowing contents return the extension. Without
1010 /// an extension and unknown contents we return "user"
1011 string const getExtFromContents(string const & filename)
1014 if (filename.empty() || !IsFileReadable(filename))
1017 ifstream ifs(filename.c_str());
1019 // Couldn't open file...
1023 string const gzipStamp = "\037\213\010\010";
1026 string const zipStamp = "PK";
1029 string const compressStamp = "\037\177";
1031 // Maximum strings to read
1032 int const max_count = 50;
1036 bool firstLine = true;
1037 while ((count++ < max_count) && format.empty()) {
1039 lyxerr[Debug::GRAPHICS]
1040 << "filetools(getExtFromContents)\n"
1041 << "\tFile type not recognised before EOF!"
1048 lyxerr[Debug::GRAPHICS] << "Scanstring: " << str.substr(0,60)
1051 string const stamp = str.substr(0,2);
1052 if (firstLine && str.size() >= 2) {
1053 // at first we check for a zipped file, because this
1054 // information is saved in the first bytes of the file!
1055 // also some graphic formats which save the information
1056 // in the first line, too.
1057 if (prefixIs(str, gzipStamp)) {
1060 } else if (stamp == zipStamp) {
1063 } else if (stamp == compressStamp) {
1064 format = "compress";
1066 // the graphics part
1067 } else if (stamp == "BM") {
1070 } else if (stamp == "\001\332") {
1074 // Don't need to use str.at(0), str.at(1) because
1075 // we already know that str.size() >= 2
1076 } else if (str[0] == 'P') {
1092 } else if ((stamp == "II") || (stamp == "MM")) {
1095 } else if (prefixIs(str,"%TGIF")) {
1098 } else if (prefixIs(str,"GIF")) {
1101 } else if (str.size() > 3) {
1102 int const c = ((str[0] << 24) & (str[1] << 16) &
1103 (str[2] << 8) & str[3]);
1112 if (!format.empty())
1114 else if (contains(str,"EPSF"))
1115 // dummy, if we have wrong file description like
1116 // %!PS-Adobe-2.0EPSF"
1119 else if (contains(str,"Grace"))
1122 else if (contains(str,"JFIF"))
1125 else if (contains(str,"%PDF"))
1128 else if (contains(str,"PNG"))
1131 else if (contains(str,"%!PS-Adobe")) {
1134 if (contains(str,"EPSF"))
1140 else if (contains(str,"_bits[]"))
1143 else if (contains(str,"XPM") || contains(str, "static char *"))
1146 else if (contains(str,"BITPIX"))
1150 if (!format.empty()) {
1151 lyxerr[Debug::GRAPHICS]
1152 << "Recognised Fileformat: " << format << endl;
1156 string const ext(GetExtension(filename));
1157 lyxerr[Debug::GRAPHICS]
1158 << "filetools(getExtFromContents)\n"
1159 << "\tCouldn't find a known Type!\n";
1161 lyxerr[Debug::GRAPHICS]
1162 << "\twill take the file extension -> "
1166 lyxerr[Debug::GRAPHICS]
1167 << "\twill use ext or a \"user\" defined format" << endl;
1173 /// check for zipped file
1174 bool zippedFile(string const & name)
1176 string const type = getExtFromContents(name);
1177 if (contains("gzip zip compress", type) && !type.empty())
1183 string const unzipFile(string const & zipped_file)
1185 string const file = ChangeExtension(zipped_file, string());
1186 string const tempfile = lyx::tempName(string(), file);
1188 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1190 one.startscript(Systemcall::Wait, command);
1191 // test that command was executed successfully (anon)
1192 // yes, please do. (Lgb)
1197 // Creates a nice compact path for displaying
1199 MakeDisplayPath (string const & path, unsigned int threshold)
1201 string::size_type const l1 = path.length();
1203 // First, we try a relative path compared to home
1204 string const home(GetEnvPath("HOME"));
1205 string relhome = MakeRelPath(path, home);
1207 string::size_type l2 = relhome.length();
1211 // If we backup from home or don't have a relative path,
1212 // this try is no good
1213 if (prefixIs(relhome, "../") || os::is_absolute_path(relhome)) {
1214 // relative path was no good, just use the original path
1221 // Is the path too long?
1222 if (l2 > threshold) {
1228 while (relhome.length() > threshold)
1229 relhome = split(relhome, temp, '/');
1231 // Did we shortend everything away?
1232 if (relhome.empty()) {
1233 // Yes, filename in itself is too long.
1234 // Pick the start and the end of the filename.
1235 relhome = OnlyFilename(path);
1236 string const head = relhome.substr(0, threshold/2 - 3);
1238 l2 = relhome.length();
1240 relhome.substr(l2 - threshold/2 - 2, l2 - 1);
1241 relhome = head + "..." + tail;
1244 return prefix + relhome;
1248 bool LyXReadLink(string const & File, string & Link)
1250 char LinkBuffer[512];
1251 // Should be PATH_MAX but that needs autconf support
1252 int const nRead = ::readlink(File.c_str(),
1253 LinkBuffer, sizeof(LinkBuffer) - 1);
1256 LinkBuffer[nRead] = '\0'; // terminator
1264 typedef pair<int, string> cmdret;
1266 cmdret const do_popen(string const & cmd)
1268 // One question is if we should use popen or
1269 // create our own popen based on fork, exec, pipe
1270 // of course the best would be to have a
1271 // pstream (process stream), with the
1272 // variants ipstream, opstream
1274 FILE * inf = ::popen(cmd.c_str(), os::read_mode());
1276 // (Claus Hentschel) Check if popen was succesful ;-)
1278 return make_pair(-1, string());
1283 ret += static_cast<char>(c);
1286 int const pret = pclose(inf);
1287 return make_pair(pret, ret);
1293 string const findtexfile(string const & fil, string const & /*format*/)
1295 /* There is no problem to extend this function too use other
1296 methods to look for files. It could be setup to look
1297 in environment paths and also if wanted as a last resort
1298 to a recursive find. One of the easier extensions would
1299 perhaps be to use the LyX file lookup methods. But! I am
1300 going to implement this until I see some demand for it.
1304 // If the file can be found directly, we just return a
1305 // absolute path version of it.
1306 if (FileInfo(fil).exist())
1307 return MakeAbsPath(fil);
1309 // No we try to find it using kpsewhich.
1310 // It seems from the kpsewhich manual page that it is safe to use
1311 // kpsewhich without --format: "When the --format option is not
1312 // given, the search path used when looking for a file is inferred
1313 // from the name given, by looking for a known extension. If no
1314 // known extension is found, the search path for TeX source files
1316 // However, we want to take advantage of the format sine almost all
1317 // the different formats has environment variables that can be used
1318 // to controll which paths to search. f.ex. bib looks in
1319 // BIBINPUTS and TEXBIB. Small list follows:
1320 // bib - BIBINPUTS, TEXBIB
1322 // graphic/figure - TEXPICTS, TEXINPUTS
1323 // ist - TEXINDEXSTYLE, INDEXSTYLE
1324 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1326 // tfm - TFMFONTS, TEXFONTS
1327 // This means that to use kpsewhich in the best possible way we
1328 // should help it by setting additional path in the approp. envir.var.
1329 string const kpsecmd = "kpsewhich " + fil;
1331 cmdret const c = do_popen(kpsecmd);
1333 lyxerr[Debug::LATEX] << "kpse status = " << c.first << "\n"
1334 << "kpse result = `" << strip(c.second, '\n')
1337 return os::internal_path(strip(strip(c.second, '\n'), '\r'));
1343 void removeAutosaveFile(string const & filename)
1345 string a = OnlyPath(filename);
1347 a += OnlyFilename(filename);
1349 FileInfo const fileinfo(a);
1350 if (fileinfo.exist()) {
1351 if (lyx::unlink(a) != 0) {
1352 Alert::err_alert(_("Could not delete auto-save file!"), a);
1358 string const readBB_from_PSFile(string const & file)
1360 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1361 // It seems that every command in the header has an own line,
1362 // getline() should work for all files.
1363 // On the other hand some plot programs write the bb at the
1364 // end of the file. Than we have in the header:
1365 // %%BoundingBox: (atend)
1366 // In this case we must check the end.
1367 string const file_ = zippedFile(file) ?
1368 string(unzipFile(file)) : string(file);
1369 string const format = getExtFromContents(file_);
1370 if (format != "eps" && format != "ps")
1373 std::ifstream is(file_.c_str());
1377 if (contains(s,"%%BoundingBox:") && !contains(s,"atend"))
1378 return (frontStrip(s.substr(14)));