3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * parts Copyright 1985, 1990, 1993 Free Software Foundation, Inc.
8 * \author Ivan Schreter
9 * \author Dirk Niggemann
10 * \author Asger Alstrup
11 * \author Lars Gullik Bjønnes
12 * \author Jean-Marc Lasgouttes
14 * Full author contact details are available in file CREDITS
16 * General path-mangling functions
22 #include "support/lstrings.h"
23 #include "support/systemcall.h"
25 #include "filetools.h"
27 #include "frontends/Alert.h"
29 #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>
64 #ifndef CXX_GLOBAL_CSTD
77 extern string system_lyxdir;
78 extern string build_lyxdir;
79 extern string user_lyxdir;
82 bool IsLyXFilename(string const & filename)
84 return suffixIs(ascii_lowercase(filename), ".lyx");
88 bool IsSGMLFilename(string const & filename)
90 return suffixIs(ascii_lowercase(filename), ".sgml");
94 // Substitutes spaces with underscores in filename (and path)
95 string const MakeLatexName(string const & file)
97 string name = OnlyFilename(file);
98 string const path = OnlyPath(file);
100 for (string::size_type i = 0; i < name.length(); ++i) {
101 name[i] &= 0x7f; // set 8th bit to 0
104 // ok so we scan through the string twice, but who cares.
105 string const keep("abcdefghijklmnopqrstuvwxyz"
106 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
107 "@!\"'()*+,-./0123456789:;<=>?[]`|");
109 string::size_type pos = 0;
110 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
113 return AddName(path, name);
117 // Substitutes spaces with underscores in filename (and path)
118 string const QuoteName(string const & name)
120 return (os::shell() == os::UNIX) ?
126 // Is a file readable ?
127 bool IsFileReadable(string const & path)
130 return 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,
176 bool notfound = true;
177 string tmppath = split(path, path_element, ';');
179 while (notfound && !path_element.empty()) {
180 path_element = os::slashify_path(path_element);
181 if (!suffixIs(path_element, '/'))
183 path_element = subst(path_element, "$$LyX", system_lyxdir);
184 path_element = subst(path_element, "$$User", user_lyxdir);
186 real_file = FileSearch(path_element, name, ext);
188 if (real_file.empty()) {
190 tmppath = split(tmppath, path_element, ';');
191 } while (!tmppath.empty() && path_element.empty());
197 if (ext.empty() && notfound) {
198 real_file = FileOpenSearch(path, name, "exe");
199 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
206 /// Returns a vector of all files in directory dir having extension ext.
207 vector<string> const DirList(string const & dir, string const & ext)
209 // This is a non-error checking C/system implementation
211 if (!ext.empty() && ext[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, 1, '.');
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, ";");
373 int DeleteAllFilesInDir(string const & path)
375 // I have decided that we will be using parts from the boost
376 // library. Check out http://www.boost.org/
377 // For directory access we will then use the directory_iterator.
378 // Then the code will be something like:
379 // directory_iterator dit(path);
380 // directory_iterator dend;
381 // if (dit == dend) {
382 // Alert::err_alert(_("Error! Cannot open directory:"), path);
385 // for (; dit != dend; ++dit) {
386 // string filename(*dit);
387 // if (filename == "." || filename == "..")
389 // string unlinkpath(AddName(path, filename));
390 // if (lyx::unlink(unlinkpath))
391 // Alert::err_alert(_("Error! Could not remove file:"),
395 DIR * dir = ::opendir(path.c_str());
397 Alert::err_alert (_("Error! Cannot open directory:"), path);
401 int return_value = 0;
402 while ((de = readdir(dir))) {
403 string const temp = de->d_name;
404 if (temp == "." || temp == "..")
406 string const unlinkpath = AddName (path, temp);
408 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
412 FileInfo fi(unlinkpath);
413 if (fi.isOK() && fi.isDir())
414 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
415 deleted &= (lyx::unlink(unlinkpath) == 0);
417 Alert::err_alert(_("Error! Could not remove file:"),
427 string const CreateTmpDir(string const & tempdir, string const & mask)
430 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
431 << "CreateTmpDir: mask=`" << mask << '\'' << endl;
433 string const tmpfl(lyx::tempName(tempdir, mask));
434 // lyx::tempName actually creates a file to make sure that it
435 // stays unique. So we have to delete it before we can create
436 // a dir with the same name. Note also that we are not thread
437 // safe because of the gap between unlink and mkdir. (Lgb)
438 lyx::unlink(tmpfl.c_str());
440 if (tmpfl.empty() || lyx::mkdir(tmpfl, 0700)) {
441 Alert::err_alert(_("Error! Couldn't create temporary directory:"),
445 return MakeAbsPath(tmpfl);
449 int DestroyTmpDir(string const & tmpdir, bool Allfiles)
454 if (Allfiles && DeleteAllFilesInDir(tmpdir)) {
457 if (lyx::rmdir(tmpdir)) {
458 Alert::err_alert(_("Error! Couldn't delete temporary directory:"),
468 string const CreateBufferTmpDir(string const & pathfor)
471 static string const tmpdir(pathfor.empty() ? os::getTmpDir() : pathfor);
472 // We are in our own directory. Why bother to mangle name?
473 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
475 string const tmpfl = tmpdir + "/lyx_tmpbuf" + tostr(count++);
476 if (lyx::mkdir(tmpfl, 0777)) {
477 Alert::err_alert(_("Error! Couldn't create temporary directory:"),
485 int DestroyBufferTmpDir(string const & tmpdir)
487 return DestroyTmpDir(tmpdir, true);
491 string const CreateLyXTmpDir(string const & deflt)
493 if ((!deflt.empty()) && (deflt != "/tmp")) {
494 if (lyx::mkdir(deflt, 0777)) {
498 return CreateTmpDir(deflt, "lyx_tmpdir");
505 return CreateTmpDir("/tmp", "lyx_tmpdir");
510 // FIXME: no need for separate method like this ...
511 int DestroyLyXTmpDir(string const & tmpdir)
513 return DestroyTmpDir(tmpdir, true);
517 // Creates directory. Returns true if succesfull
518 bool createDirectory(string const & path, int permission)
520 string temp(rtrim(os::slashify_path(path), "/"));
523 Alert::alert(_("Internal error!"),
524 _("Call to createDirectory with invalid name"));
528 if (lyx::mkdir(temp, permission)) {
529 Alert::err_alert (_("Error! Couldn't create directory:"), temp);
536 // Strip filename from path name
537 string const OnlyPath(string const & Filename)
539 // If empty filename, return empty
540 if (Filename.empty()) return Filename;
542 // Find last / or start of filename
543 string::size_type j = Filename.rfind('/');
544 if (j == string::npos)
546 return Filename.substr(0, j + 1);
550 // Convert relative path into absolute path based on a basepath.
551 // If relpath is absolute, just use that.
552 // If basepath is empty, use CWD as base.
553 string const MakeAbsPath(string const & RelPath, string const & BasePath)
555 // checks for already absolute path
556 if (os::is_absolute_path(RelPath))
559 // Copies given paths
560 string TempRel(os::slashify_path(RelPath));
561 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
562 TempRel = subst(TempRel, "//", "/");
566 if (os::is_absolute_path(BasePath))
569 TempBase = AddPath(lyx::getcwd(), BasePath);
571 // Handle /./ at the end of the path
572 while (suffixIs(TempBase, "/./"))
573 TempBase.erase(TempBase.length() - 2);
575 // processes relative path
576 string RTemp(TempRel);
579 while (!RTemp.empty()) {
581 RTemp = split(RTemp, Temp, '/');
583 if (Temp == ".") continue;
585 // Remove one level of TempBase
586 string::difference_type i = TempBase.length() - 2;
589 while (i > 0 && TempBase[i] != '/') --i;
593 while (i > 2 && TempBase[i] != '/') --i;
596 TempBase.erase(i, string::npos);
599 } else if (Temp.empty() && !RTemp.empty()) {
600 TempBase = os::current_root() + RTemp;
603 // Add this piece to TempBase
604 if (!suffixIs(TempBase, '/'))
610 // returns absolute path
611 return os::slashify_path(TempBase);
615 // Correctly append filename to the pathname.
616 // If pathname is '.', then don't use pathname.
617 // Chops any path of filename.
618 string const AddName(string const & path, string const & fname)
621 string const basename(OnlyFilename(fname));
625 if (path != "." && path != "./" && !path.empty()) {
626 buf = os::slashify_path(path);
627 if (!suffixIs(path, '/'))
631 return buf + basename;
635 // Strips path from filename
636 string const OnlyFilename(string const & fname)
641 string::size_type j = fname.rfind('/');
642 if (j == string::npos) // no '/' in fname
646 return fname.substr(j + 1);
650 /// Returns true is path is absolute
651 bool AbsolutePath(string const & path)
653 return os::is_absolute_path(path);
658 // Create absolute path. If impossible, don't do anything
659 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
660 string const ExpandPath(string const & path)
662 // checks for already absolute path
663 string RTemp(ReplaceEnvironmentPath(path));
664 if (os::is_absolute_path(RTemp))
668 string const copy(RTemp);
671 RTemp = split(RTemp, Temp, '/');
674 return lyx::getcwd() /*GetCWD()*/ + '/' + RTemp;
677 return GetEnvPath("HOME") + '/' + RTemp;
680 return MakeAbsPath(copy);
682 // Don't know how to handle this
688 // Constracts path/../path
689 // Can't handle "../../" or "/../" (Asger)
690 string const NormalizePath(string const & path)
696 if (os::is_absolute_path(path))
699 // Make implicit current directory explicit
702 while (!RTemp.empty()) {
704 RTemp = split(RTemp, Temp, '/');
708 } else if (Temp == "..") {
709 // Remove one level of TempBase
710 string::difference_type i = TempBase.length() - 2;
711 while (i > 0 && TempBase[i] != '/')
713 if (i >= 0 && TempBase[i] == '/')
714 TempBase.erase(i + 1, string::npos);
718 TempBase += Temp + '/';
722 // returns absolute path
727 string const GetFileContents(string const & fname)
729 FileInfo finfo(fname);
731 ifstream ifs(fname.c_str());
736 return STRCONV(ofs.str());
739 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
745 // Search ${...} as Variable-Name inside the string and replace it with
746 // the denoted environmentvariable
747 // Allow Variables according to
748 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
751 string const ReplaceEnvironmentPath(string const & path)
754 // CompareChar: Environment variables starts with this character
755 // PathChar: Next path component start with this character
756 // while CompareChar found do:
757 // Split String with PathChar
758 // Search Environmentvariable
759 // if found: Replace Strings
761 char const CompareChar = '$';
762 char const FirstChar = '{';
763 char const EndChar = '}';
764 char const UnderscoreChar = '_';
765 string EndString; EndString += EndChar;
766 string FirstString; FirstString += FirstChar;
767 string CompareString; CompareString += CompareChar;
768 string const RegExp("*}*"); // Exist EndChar inside a String?
770 // first: Search for a '$' - Sign.
772 string result1; //(copy); // for split-calls
773 string result0 = split(path, result1, CompareChar);
774 while (!result0.empty()) {
775 string copy1(result0); // contains String after $
777 // Check, if there is an EndChar inside original String.
779 if (!regexMatch(copy1, RegExp)) {
780 // No EndChar inside. So we are finished
781 result1 += CompareString + result0;
787 string res0 = split(copy1, res1, EndChar);
788 // Now res1 holds the environmentvariable
789 // First, check, if Contents is ok.
790 if (res1.empty()) { // No environmentvariable. Continue Loop.
791 result1 += CompareString + FirstString;
795 // check contents of res1
796 char const * res1_contents = res1.c_str();
797 if (*res1_contents != FirstChar) {
798 // Again No Environmentvariable
799 result1 += CompareString;
803 // Check for variable names
804 // Situation ${} is detected as "No Environmentvariable"
805 char const * cp1 = res1_contents + 1;
806 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
808 while (*cp1 && result) {
809 result = isalnum(*cp1) ||
810 (*cp1 == UnderscoreChar);
815 // no correct variable name
816 result1 += CompareString + res1 + EndString;
817 result0 = split(res0, res1, CompareChar);
822 string env(GetEnv(res1_contents + 1));
824 // Congratulations. Environmentvariable found
827 result1 += CompareString + res1 + EndString;
830 result0 = split(res0, res1, CompareChar);
837 // Make relative path out of two absolute paths
838 string const MakeRelPath(string const & abspath, string const & basepath)
839 // Makes relative path out of absolute path. If it is deeper than basepath,
840 // it's easy. If basepath and abspath share something (they are all deeper
841 // than some directory), it'll be rendered using ..'s. If they are completely
842 // different, then the absolute path will be used as relative path.
844 string::size_type const abslen = abspath.length();
845 string::size_type const baselen = basepath.length();
847 string::size_type i = os::common_path(abspath, basepath);
850 // actually no match - cannot make it relative
854 // Count how many dirs there are in basepath above match
855 // and append as many '..''s into relpath
857 string::size_type j = i;
858 while (j < baselen) {
859 if (basepath[j] == '/') {
860 if (j + 1 == baselen)
867 // Append relative stuff from common directory to abspath
868 if (abspath[i] == '/')
870 for (; i < abslen; ++i)
873 if (suffixIs(buf, '/'))
874 buf.erase(buf.length() - 1);
875 // Substitute empty with .
882 // Append sub-directory(ies) to a path in an intelligent way
883 string const AddPath(string const & path, string const & path_2)
886 string const path2 = os::slashify_path(path_2);
888 if (!path.empty() && path != "." && path != "./") {
889 buf = os::slashify_path(path);
890 if (path[path.length() - 1] != '/')
894 if (!path2.empty()) {
895 string::size_type const p2start = path2.find_first_not_of('/');
896 string::size_type const p2end = path2.find_last_not_of('/');
897 string const tmp = path2.substr(p2start, p2end - p2start + 1);
905 Change extension of oldname to extension.
906 Strips path off if no_path == true.
907 If no extension on oldname, just appends.
909 string const ChangeExtension(string const & oldname, string const & extension)
911 string::size_type const last_slash = oldname.rfind('/');
912 string::size_type last_dot = oldname.rfind('.');
913 if (last_dot < last_slash && last_slash != string::npos)
914 last_dot = string::npos;
917 // Make sure the extension starts with a dot
918 if (!extension.empty() && extension[0] != '.')
919 ext= '.' + extension;
923 return os::slashify_path(oldname.substr(0, last_dot) + ext);
927 /// Return the extension of the file (not including the .)
928 string const GetExtension(string const & name)
930 string::size_type const last_slash = name.rfind('/');
931 string::size_type const last_dot = name.rfind('.');
932 if (last_dot != string::npos &&
933 (last_slash == string::npos || last_dot > last_slash))
934 return name.substr(last_dot + 1,
935 name.length() - (last_dot + 1));
940 // the different filetypes and what they contain in one of the first lines
941 // (dots are any characters). (Herbert 20020131)
944 // EPS %!PS-Adobe-3.0 EPSF...
951 // PBM P1... or P4 (B/W)
952 // PGM P2... or P5 (Grayscale)
953 // PPM P3... or P6 (color)
954 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
955 // SGI \001\332... (decimal 474)
957 // TIFF II... or MM...
959 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
960 // ...static char *...
961 // XWD \000\000\000\151 (0x00006900) decimal 105
963 // GZIP \037\213\010\010... http://www.ietf.org/rfc/rfc1952.txt
964 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
965 // Z \037\177 UNIX compress
967 /// return the "extension" which belongs to the contents.
968 /// for no knowing contents return the extension. Without
969 /// an extension and unknown contents we return "user"
970 string const getExtFromContents(string const & filename)
973 if (filename.empty() || !IsFileReadable(filename))
977 ifstream ifs(filename.c_str());
979 // Couldn't open file...
983 string const gzipStamp = "\037\213\010\010";
986 string const zipStamp = "PK";
989 string const compressStamp = "\037\177";
991 // Maximum strings to read
992 int const max_count = 50;
996 bool firstLine = true;
997 while ((count++ < max_count) && format.empty()) {
999 lyxerr[Debug::GRAPHICS]
1000 << "filetools(getExtFromContents)\n"
1001 << "\tFile type not recognised before EOF!"
1007 string const stamp = str.substr(0,2);
1008 if (firstLine && str.size() >= 2) {
1009 // at first we check for a zipped file, because this
1010 // information is saved in the first bytes of the file!
1011 // also some graphic formats which save the information
1012 // in the first line, too.
1013 if (prefixIs(str, gzipStamp)) {
1016 } else if (stamp == zipStamp) {
1019 } else if (stamp == compressStamp) {
1020 format = "compress";
1022 // the graphics part
1023 } else if (stamp == "BM") {
1026 } else if (stamp == "\001\332") {
1030 // Don't need to use str.at(0), str.at(1) because
1031 // we already know that str.size() >= 2
1032 } else if (str[0] == 'P') {
1048 } else if ((stamp == "II") || (stamp == "MM")) {
1051 } else if (prefixIs(str,"%TGIF")) {
1054 } else if (prefixIs(str,"#FIG")) {
1057 } else if (prefixIs(str,"GIF")) {
1060 } else if (str.size() > 3) {
1061 int const c = ((str[0] << 24) & (str[1] << 16) &
1062 (str[2] << 8) & str[3]);
1071 if (!format.empty())
1073 else if (contains(str,"EPSF"))
1074 // dummy, if we have wrong file description like
1075 // %!PS-Adobe-2.0EPSF"
1078 else if (contains(str,"Grace"))
1081 else if (contains(str,"JFIF"))
1084 else if (contains(str,"%PDF"))
1087 else if (contains(str,"PNG"))
1090 else if (contains(str,"%!PS-Adobe")) {
1093 if (contains(str,"EPSF"))
1099 else if (contains(str,"_bits[]"))
1102 else if (contains(str,"XPM") || contains(str, "static char *"))
1105 else if (contains(str,"BITPIX"))
1109 if (!format.empty()) {
1110 lyxerr[Debug::GRAPHICS]
1111 << "Recognised Fileformat: " << format << endl;
1115 string const ext(GetExtension(filename));
1116 lyxerr[Debug::GRAPHICS]
1117 << "filetools(getExtFromContents)\n"
1118 << "\tCouldn't find a known Type!\n";
1120 lyxerr[Debug::GRAPHICS]
1121 << "\twill take the file extension -> "
1125 lyxerr[Debug::GRAPHICS]
1126 << "\twill use ext or a \"user\" defined format" << endl;
1132 /// check for zipped file
1133 bool zippedFile(string const & name)
1135 string const type = getExtFromContents(name);
1136 if (contains("gzip zip compress", type) && !type.empty())
1142 string const unzipFile(string const & zipped_file)
1144 string const file = ChangeExtension(zipped_file, string());
1145 string const tempfile = lyx::tempName(string(), file);
1147 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1149 one.startscript(Systemcall::Wait, command);
1150 // test that command was executed successfully (anon)
1151 // yes, please do. (Lgb)
1156 // Creates a nice compact path for displaying
1158 MakeDisplayPath (string const & path, unsigned int threshold)
1160 string::size_type const l1 = path.length();
1162 // First, we try a relative path compared to home
1163 string const home(GetEnvPath("HOME"));
1164 string relhome = MakeRelPath(path, home);
1166 string::size_type l2 = relhome.length();
1170 // If we backup from home or don't have a relative path,
1171 // this try is no good
1172 if (prefixIs(relhome, "../") || os::is_absolute_path(relhome)) {
1173 // relative path was no good, just use the original path
1180 // Is the path too long?
1181 if (l2 > threshold) {
1187 while (relhome.length() > threshold)
1188 relhome = split(relhome, temp, '/');
1190 // Did we shortend everything away?
1191 if (relhome.empty()) {
1192 // Yes, filename in itself is too long.
1193 // Pick the start and the end of the filename.
1194 relhome = OnlyFilename(path);
1195 string const head = relhome.substr(0, threshold/2 - 3);
1197 l2 = relhome.length();
1199 relhome.substr(l2 - threshold/2 - 2, l2 - 1);
1200 relhome = head + "..." + tail;
1203 return prefix + relhome;
1207 bool LyXReadLink(string const & file, string & link, bool resolve)
1209 char linkbuffer[512];
1210 // Should be PATH_MAX but that needs autconf support
1211 int const nRead = ::readlink(file.c_str(),
1212 linkbuffer, sizeof(linkbuffer) - 1);
1215 linkbuffer[nRead] = '\0'; // terminator
1217 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1224 cmd_ret const RunCommand(string const & cmd)
1226 // One question is if we should use popen or
1227 // create our own popen based on fork, exec, pipe
1228 // of course the best would be to have a
1229 // pstream (process stream), with the
1230 // variants ipstream, opstream
1232 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1234 // (Claus Hentschel) Check if popen was succesful ;-)
1236 return make_pair(-1, string());
1241 ret += static_cast<char>(c);
1244 int const pret = pclose(inf);
1245 return make_pair(pret, ret);
1249 string const findtexfile(string const & fil, string const & /*format*/)
1251 /* There is no problem to extend this function too use other
1252 methods to look for files. It could be setup to look
1253 in environment paths and also if wanted as a last resort
1254 to a recursive find. One of the easier extensions would
1255 perhaps be to use the LyX file lookup methods. But! I am
1256 going to implement this until I see some demand for it.
1260 // If the file can be found directly, we just return a
1261 // absolute path version of it.
1262 if (FileInfo(fil).exist())
1263 return MakeAbsPath(fil);
1265 // No we try to find it using kpsewhich.
1266 // It seems from the kpsewhich manual page that it is safe to use
1267 // kpsewhich without --format: "When the --format option is not
1268 // given, the search path used when looking for a file is inferred
1269 // from the name given, by looking for a known extension. If no
1270 // known extension is found, the search path for TeX source files
1272 // However, we want to take advantage of the format sine almost all
1273 // the different formats has environment variables that can be used
1274 // to controll which paths to search. f.ex. bib looks in
1275 // BIBINPUTS and TEXBIB. Small list follows:
1276 // bib - BIBINPUTS, TEXBIB
1278 // graphic/figure - TEXPICTS, TEXINPUTS
1279 // ist - TEXINDEXSTYLE, INDEXSTYLE
1280 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1282 // tfm - TFMFONTS, TEXFONTS
1283 // This means that to use kpsewhich in the best possible way we
1284 // should help it by setting additional path in the approp. envir.var.
1285 string const kpsecmd = "kpsewhich " + fil;
1287 cmd_ret const c = RunCommand(kpsecmd);
1289 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1290 << "kpse result = `" << rtrim(c.second, "\n")
1293 return os::internal_path(rtrim(c.second, "\n\r"));
1299 void removeAutosaveFile(string const & filename)
1301 string a = OnlyPath(filename);
1303 a += OnlyFilename(filename);
1305 FileInfo const fileinfo(a);
1306 if (fileinfo.exist()) {
1307 if (lyx::unlink(a) != 0) {
1308 Alert::err_alert(_("Could not delete auto-save file!"), a);
1314 void readBB_lyxerrMessage(string const & file, bool & zipped,
1315 string const & message)
1317 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1318 << message << std::endl;
1324 string const readBB_from_PSFile(string const & file)
1326 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1327 // It seems that every command in the header has an own line,
1328 // getline() should work for all files.
1329 // On the other hand some plot programs write the bb at the
1330 // end of the file. Than we have in the header:
1331 // %%BoundingBox: (atend)
1332 // In this case we must check the end.
1333 bool zipped = zippedFile(file);
1334 string const file_ = zipped ?
1335 string(unzipFile(file)) : string(file);
1336 string const format = getExtFromContents(file_);
1338 if (format != "eps" && format != "ps") {
1339 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1343 std::ifstream is(file_.c_str());
1347 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1348 string const bb = ltrim(s.substr(14));
1349 readBB_lyxerrMessage(file_, zipped, bb);
1353 readBB_lyxerrMessage(file_, zipped, "no bb found");