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
13 * \author Angus Leeming
15 * \author Herbert Voss
17 * This file is part of LyX, the document processor.
18 * Licence details can be found in the file COPYING.
20 * General path-mangling functions
26 #include "support/tostr.h"
27 #include "support/systemcall.h"
28 #include "support/LAssert.h"
30 #include "filetools.h"
33 #include "support/path.h" // I know it's OS/2 specific (SMiyata)
40 #include <boost/cregex.hpp>
51 // Which part of this is still necessary? (JMarc).
54 # define NAMLEN(dirent) strlen((dirent)->d_name)
56 # define dirent direct
57 # define NAMLEN(dirent) (dirent)->d_namlen
59 # include <sys/ndir.h>
69 #ifndef CXX_GLOBAL_CSTD
82 extern string system_lyxdir;
83 extern string build_lyxdir;
84 extern string user_lyxdir;
87 bool IsLyXFilename(string const & filename)
89 return suffixIs(ascii_lowercase(filename), ".lyx");
93 bool IsSGMLFilename(string const & filename)
95 return suffixIs(ascii_lowercase(filename), ".sgml");
99 // Substitutes spaces with underscores in filename (and path)
100 string const MakeLatexName(string const & file)
102 string name = OnlyFilename(file);
103 string const path = OnlyPath(file);
105 for (string::size_type i = 0; i < name.length(); ++i) {
106 name[i] &= 0x7f; // set 8th bit to 0
109 // ok so we scan through the string twice, but who cares.
110 string const keep("abcdefghijklmnopqrstuvwxyz"
111 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
112 "@!\"'()*+,-./0123456789:;<=>?[]`|");
114 string::size_type pos = 0;
115 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
118 return AddName(path, name);
122 // Substitutes spaces with underscores in filename (and path)
123 string const QuoteName(string const & name)
125 return (os::shell() == os::UNIX) ?
131 // Is a file readable ?
132 bool IsFileReadable(string const & path)
135 return file.isOK() && file.isRegular() && file.readable();
139 // Is a file read_only?
140 // return 1 read-write
142 // -1 error (doesn't exist, no access, anything else)
143 int IsFileWriteable(string const & path)
147 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
149 if (fi.readable()) // read-only
151 return -1; // everything else.
155 //returns true: dir writeable
156 // false: not writeable
157 bool IsDirWriteable(string const & path)
159 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
161 string const tmpfl(lyx::tempName(path, "lyxwritetest"));
171 // Uses a string of paths separated by ";"s to find a file to open.
172 // Can't cope with pathnames with a ';' in them. Returns full path to file.
173 // If path entry begins with $$LyX/, use system_lyxdir
174 // If path entry begins with $$User/, use user_lyxdir
175 // Example: "$$User/doc;$$LyX/doc"
176 string const FileOpenSearch(string const & path, string const & name,
181 bool notfound = true;
182 string tmppath = split(path, path_element, ';');
184 while (notfound && !path_element.empty()) {
185 path_element = os::slashify_path(path_element);
186 if (!suffixIs(path_element, '/'))
188 path_element = subst(path_element, "$$LyX", system_lyxdir);
189 path_element = subst(path_element, "$$User", user_lyxdir);
191 real_file = FileSearch(path_element, name, ext);
193 if (real_file.empty()) {
195 tmppath = split(tmppath, path_element, ';');
196 } while (!tmppath.empty() && path_element.empty());
202 if (ext.empty() && notfound) {
203 real_file = FileOpenSearch(path, name, "exe");
204 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
211 /// Returns a vector of all files in directory dir having extension ext.
212 vector<string> const DirList(string const & dir, string const & ext)
214 // This is a non-error checking C/system implementation
216 if (!ext.empty() && ext[0] != '.')
220 vector<string> dirlist;
221 DIR * dirp = ::opendir(dir.c_str());
224 << "Directory \"" << dir
225 << "\" does not exist to DirList." << endl;
230 while ((dire = ::readdir(dirp))) {
231 string const fil = dire->d_name;
232 if (suffixIs(fil, extension)) {
233 dirlist.push_back(fil);
238 /* I would have prefered to take a vector<string>& as parameter so
239 that we could avoid the copy of the vector when returning.
241 dirlist.swap(argvec);
242 to avoid the copy. (Lgb)
244 /* A C++ implementaion will look like this:
245 string extension(ext);
246 if (extension[0] != '.') extension.insert(0, 1, '.');
247 vector<string> dirlist;
248 directory_iterator dit("dir");
249 while (dit != directory_iterator()) {
250 string fil = dit->filename;
251 if (prefixIs(fil, extension)) {
252 dirlist.push_back(fil);
256 dirlist.swap(argvec);
262 // Returns the real name of file name in directory path, with optional
264 string const FileSearch(string const & path, string const & name,
267 // if `name' is an absolute path, we ignore the setting of `path'
268 // Expand Environmentvariables in 'name'
269 string const tmpname = ReplaceEnvironmentPath(name);
270 string fullname = MakeAbsPath(tmpname, path);
271 // search first without extension, then with it.
272 if (IsFileReadable(fullname))
274 else if (ext.empty())
276 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
279 if (IsFileReadable(fullname))
287 // Search the file name.ext in the subdirectory dir of
289 // 2) build_lyxdir (if not empty)
291 string const LibFileSearch(string const & dir, string const & name,
294 string fullname = FileSearch(AddPath(user_lyxdir, dir), name, ext);
295 if (!fullname.empty())
298 if (!build_lyxdir.empty())
299 fullname = FileSearch(AddPath(build_lyxdir, dir), name, ext);
300 if (!fullname.empty())
303 return FileSearch(AddPath(system_lyxdir, dir), name, ext);
308 i18nLibFileSearch(string const & dir, string const & name,
311 // this comment is from intl/dcigettext.c. We try to mimick this
313 /* The highest priority value is the `LANGUAGE' environment
314 variable. But we don't use the value if the currently
315 selected locale is the C locale. This is a GNU extension. */
317 string const lc_all = GetEnv("LC_ALL");
318 string lang = GetEnv("LANGUAGE");
319 if (lang.empty() || lc_all == "C") {
322 lang = GetEnv("LANG");
326 lang = token(lang, '_', 0);
328 if (lang.empty() || lang == "C")
329 return LibFileSearch(dir, name, ext);
331 string const tmp = LibFileSearch(dir, lang + '_' + name,
336 return LibFileSearch(dir, name, ext);
341 string const LibScriptSearch(string const & command_in)
343 string const token_scriptpath("$$s/");
345 string command = command_in;
346 // Find the starting position of "$$s/"
347 string::size_type const pos1 = command.find(token_scriptpath);
348 if (pos1 == string::npos)
350 // Find the end of the "$$s/some_script" word within command
351 string::size_type const start_script = pos1 + 4;
352 string::size_type const pos2 = command.find(' ', start_script);
353 string::size_type const size_script = pos2 == string::npos?
354 (command.size() - start_script) : pos2 - start_script;
356 // Does this script file exist?
357 string const script =
358 LibFileSearch("scripts", command.substr(start_script, size_script));
360 if (script.empty()) {
361 // Replace "$$s/" with ""
362 command.erase(pos1, 4);
364 // Replace "$$s/some_script" with "$LYX_SCRIPT_PATH/some_script"
365 string::size_type const size_replace = size_script + 4;
366 command.replace(pos1, size_replace, script);
373 string const GetEnv(string const & envname)
375 // f.ex. what about error checking?
376 char const * const ch = getenv(envname.c_str());
377 string const envstr = !ch ? "" : ch;
382 string const GetEnvPath(string const & name)
385 string const pathlist = subst(GetEnv(name), ':', ';');
387 string const pathlist = os::slashify_path(GetEnv(name));
389 return rtrim(pathlist, ";");
395 int DeleteAllFilesInDir(string const & path)
397 // I have decided that we will be using parts from the boost
398 // library. Check out http://www.boost.org/
399 // For directory access we will then use the directory_iterator.
400 // Then the code will be something like:
401 // directory_iterator dit(path);
402 // directory_iterator dend;
403 // if (dit == dend) {
406 // for (; dit != dend; ++dit) {
407 // string filename(*dit);
408 // if (filename == "." || filename == "..")
410 // string unlinkpath(AddName(path, filename));
411 // lyx::unlink(unlinkpath);
414 DIR * dir = ::opendir(path.c_str());
419 int return_value = 0;
420 while ((de = readdir(dir))) {
421 string const temp = de->d_name;
422 if (temp == "." || temp == "..")
424 string const unlinkpath = AddName (path, temp);
426 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
430 FileInfo fi(unlinkpath);
431 if (fi.isOK() && fi.isDir())
432 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
433 deleted &= (lyx::unlink(unlinkpath) == 0);
442 string const CreateTmpDir(string const & tempdir, string const & mask)
445 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
446 << "CreateTmpDir: mask=`" << mask << '\'' << endl;
448 string const tmpfl(lyx::tempName(tempdir, mask));
449 // lyx::tempName actually creates a file to make sure that it
450 // stays unique. So we have to delete it before we can create
451 // a dir with the same name. Note also that we are not thread
452 // safe because of the gap between unlink and mkdir. (Lgb)
453 lyx::unlink(tmpfl.c_str());
455 if (tmpfl.empty() || lyx::mkdir(tmpfl, 0700))
458 return MakeAbsPath(tmpfl);
464 int destroyDir(string const & tmpdir)
469 if (DeleteAllFilesInDir(tmpdir))
472 if (lyx::rmdir(tmpdir))
479 string const CreateBufferTmpDir(string const & pathfor)
482 static string const tmpdir(pathfor.empty() ? os::getTmpDir() : pathfor);
483 // We are in our own directory. Why bother to mangle name?
484 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
486 string const tmpfl = tmpdir + "/lyx_tmpbuf" + tostr(count++);
487 if (lyx::mkdir(tmpfl, 0777)) {
494 string const CreateLyXTmpDir(string const & deflt)
496 if ((!deflt.empty()) && (deflt != "/tmp")) {
497 if (lyx::mkdir(deflt, 0777)) {
501 return CreateTmpDir(deflt, "lyx_tmpdir");
508 return CreateTmpDir("/tmp", "lyx_tmpdir");
513 bool createDirectory(string const & path, int permission)
515 string temp(rtrim(os::slashify_path(path), "/"));
517 lyx::Assert(!temp.empty());
519 if (lyx::mkdir(temp, permission))
526 // Strip filename from path name
527 string const OnlyPath(string const & Filename)
529 // If empty filename, return empty
530 if (Filename.empty()) return Filename;
532 // Find last / or start of filename
533 string::size_type j = Filename.rfind('/');
534 if (j == string::npos)
536 return Filename.substr(0, j + 1);
540 // Convert relative path into absolute path based on a basepath.
541 // If relpath is absolute, just use that.
542 // If basepath is empty, use CWD as base.
543 string const MakeAbsPath(string const & RelPath, string const & BasePath)
545 // checks for already absolute path
546 if (os::is_absolute_path(RelPath))
549 // Copies given paths
550 string TempRel(os::slashify_path(RelPath));
551 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
552 TempRel = subst(TempRel, "//", "/");
556 if (os::is_absolute_path(BasePath))
559 TempBase = AddPath(lyx::getcwd(), BasePath);
561 // Handle /./ at the end of the path
562 while (suffixIs(TempBase, "/./"))
563 TempBase.erase(TempBase.length() - 2);
565 // processes relative path
566 string RTemp(TempRel);
569 while (!RTemp.empty()) {
571 RTemp = split(RTemp, Temp, '/');
573 if (Temp == ".") continue;
575 // Remove one level of TempBase
576 string::difference_type i = TempBase.length() - 2;
579 while (i > 0 && TempBase[i] != '/') --i;
583 while (i > 2 && TempBase[i] != '/') --i;
586 TempBase.erase(i, string::npos);
589 } else if (Temp.empty() && !RTemp.empty()) {
590 TempBase = os::current_root() + RTemp;
593 // Add this piece to TempBase
594 if (!suffixIs(TempBase, '/'))
600 // returns absolute path
601 return os::slashify_path(TempBase);
605 // Correctly append filename to the pathname.
606 // If pathname is '.', then don't use pathname.
607 // Chops any path of filename.
608 string const AddName(string const & path, string const & fname)
611 string const basename(OnlyFilename(fname));
615 if (path != "." && path != "./" && !path.empty()) {
616 buf = os::slashify_path(path);
617 if (!suffixIs(path, '/'))
621 return buf + basename;
625 // Strips path from filename
626 string const OnlyFilename(string const & fname)
631 string::size_type j = fname.rfind('/');
632 if (j == string::npos) // no '/' in fname
636 return fname.substr(j + 1);
640 /// Returns true is path is absolute
641 bool AbsolutePath(string const & path)
643 return os::is_absolute_path(path);
648 // Create absolute path. If impossible, don't do anything
649 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
650 string const ExpandPath(string const & path)
652 // checks for already absolute path
653 string RTemp(ReplaceEnvironmentPath(path));
654 if (os::is_absolute_path(RTemp))
658 string const copy(RTemp);
661 RTemp = split(RTemp, Temp, '/');
664 return lyx::getcwd() /*GetCWD()*/ + '/' + RTemp;
667 return GetEnvPath("HOME") + '/' + RTemp;
670 return MakeAbsPath(copy);
672 // Don't know how to handle this
678 // Constracts path/../path
679 // Can't handle "../../" or "/../" (Asger)
680 // Also converts paths like /foo//bar ==> /foo/bar
681 string const NormalizePath(string const & path)
687 if (os::is_absolute_path(path))
690 // Make implicit current directory explicit
693 // Normalise paths like /foo//bar ==> /foo/bar
694 boost::RegEx regex("/{2,}");
695 RTemp = regex.Merge(RTemp, "/");
697 while (!RTemp.empty()) {
699 RTemp = split(RTemp, Temp, '/');
703 } else if (Temp == "..") {
704 // Remove one level of TempBase
705 string::difference_type i = TempBase.length() - 2;
706 while (i > 0 && TempBase[i] != '/')
708 if (i >= 0 && TempBase[i] == '/')
709 TempBase.erase(i + 1, string::npos);
713 TempBase += Temp + '/';
717 // returns absolute path
722 string const GetFileContents(string const & fname)
724 FileInfo finfo(fname);
726 ifstream ifs(fname.c_str());
731 return STRCONV(ofs.str());
734 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
740 // Search ${...} as Variable-Name inside the string and replace it with
741 // the denoted environmentvariable
742 // Allow Variables according to
743 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
746 string const ReplaceEnvironmentPath(string const & path)
749 // CompareChar: Environment variables starts with this character
750 // PathChar: Next path component start with this character
751 // while CompareChar found do:
752 // Split String with PathChar
753 // Search Environmentvariable
754 // if found: Replace Strings
756 char const CompareChar = '$';
757 char const FirstChar = '{';
758 char const EndChar = '}';
759 char const UnderscoreChar = '_';
760 string EndString; EndString += EndChar;
761 string FirstString; FirstString += FirstChar;
762 string CompareString; CompareString += CompareChar;
763 string const RegExp("*}*"); // Exist EndChar inside a String?
765 // first: Search for a '$' - Sign.
767 string result1; //(copy); // for split-calls
768 string result0 = split(path, result1, CompareChar);
769 while (!result0.empty()) {
770 string copy1(result0); // contains String after $
772 // Check, if there is an EndChar inside original String.
774 if (!regexMatch(copy1, RegExp)) {
775 // No EndChar inside. So we are finished
776 result1 += CompareString + result0;
782 string res0 = split(copy1, res1, EndChar);
783 // Now res1 holds the environmentvariable
784 // First, check, if Contents is ok.
785 if (res1.empty()) { // No environmentvariable. Continue Loop.
786 result1 += CompareString + FirstString;
790 // check contents of res1
791 char const * res1_contents = res1.c_str();
792 if (*res1_contents != FirstChar) {
793 // Again No Environmentvariable
794 result1 += CompareString;
798 // Check for variable names
799 // Situation ${} is detected as "No Environmentvariable"
800 char const * cp1 = res1_contents + 1;
801 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
803 while (*cp1 && result) {
804 result = isalnum(*cp1) ||
805 (*cp1 == UnderscoreChar);
810 // no correct variable name
811 result1 += CompareString + res1 + EndString;
812 result0 = split(res0, res1, CompareChar);
817 string env(GetEnv(res1_contents + 1));
819 // Congratulations. Environmentvariable found
822 result1 += CompareString + res1 + EndString;
825 result0 = split(res0, res1, CompareChar);
832 // Make relative path out of two absolute paths
833 string const MakeRelPath(string const & abspath, string const & basepath)
834 // Makes relative path out of absolute path. If it is deeper than basepath,
835 // it's easy. If basepath and abspath share something (they are all deeper
836 // than some directory), it'll be rendered using ..'s. If they are completely
837 // different, then the absolute path will be used as relative path.
839 string::size_type const abslen = abspath.length();
840 string::size_type const baselen = basepath.length();
842 string::size_type i = os::common_path(abspath, basepath);
845 // actually no match - cannot make it relative
849 // Count how many dirs there are in basepath above match
850 // and append as many '..''s into relpath
852 string::size_type j = i;
853 while (j < baselen) {
854 if (basepath[j] == '/') {
855 if (j + 1 == baselen)
862 // Append relative stuff from common directory to abspath
863 if (abspath[i] == '/')
865 for (; i < abslen; ++i)
868 if (suffixIs(buf, '/'))
869 buf.erase(buf.length() - 1);
870 // Substitute empty with .
877 // Append sub-directory(ies) to a path in an intelligent way
878 string const AddPath(string const & path, string const & path_2)
881 string const path2 = os::slashify_path(path_2);
883 if (!path.empty() && path != "." && path != "./") {
884 buf = os::slashify_path(path);
885 if (path[path.length() - 1] != '/')
889 if (!path2.empty()) {
890 string::size_type const p2start = path2.find_first_not_of('/');
891 string::size_type const p2end = path2.find_last_not_of('/');
892 string const tmp = path2.substr(p2start, p2end - p2start + 1);
900 Change extension of oldname to extension.
901 Strips path off if no_path == true.
902 If no extension on oldname, just appends.
904 string const ChangeExtension(string const & oldname, string const & extension)
906 string::size_type const last_slash = oldname.rfind('/');
907 string::size_type last_dot = oldname.rfind('.');
908 if (last_dot < last_slash && last_slash != string::npos)
909 last_dot = string::npos;
912 // Make sure the extension starts with a dot
913 if (!extension.empty() && extension[0] != '.')
914 ext= '.' + extension;
918 return os::slashify_path(oldname.substr(0, last_dot) + ext);
922 /// Return the extension of the file (not including the .)
923 string const GetExtension(string const & name)
925 string::size_type const last_slash = name.rfind('/');
926 string::size_type const last_dot = name.rfind('.');
927 if (last_dot != string::npos &&
928 (last_slash == string::npos || last_dot > last_slash))
929 return name.substr(last_dot + 1,
930 name.length() - (last_dot + 1));
935 // the different filetypes and what they contain in one of the first lines
936 // (dots are any characters). (Herbert 20020131)
939 // EPS %!PS-Adobe-3.0 EPSF...
946 // PBM P1... or P4 (B/W)
947 // PGM P2... or P5 (Grayscale)
948 // PPM P3... or P6 (color)
949 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
950 // SGI \001\332... (decimal 474)
952 // TIFF II... or MM...
954 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
955 // ...static char *...
956 // XWD \000\000\000\151 (0x00006900) decimal 105
958 // GZIP \037\213\010\010... http://www.ietf.org/rfc/rfc1952.txt
959 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
960 // Z \037\177 UNIX compress
962 /// return the "extension" which belongs to the contents.
963 /// for no knowing contents return the extension. Without
964 /// an extension and unknown contents we return "user"
965 string const getExtFromContents(string const & filename)
968 if (filename.empty() || !IsFileReadable(filename))
972 ifstream ifs(filename.c_str());
974 // Couldn't open file...
978 string const gzipStamp = "\037\213\010\010";
981 string const zipStamp = "PK";
984 string const compressStamp = "\037\177";
986 // Maximum strings to read
987 int const max_count = 50;
991 bool firstLine = true;
992 while ((count++ < max_count) && format.empty()) {
994 lyxerr[Debug::GRAPHICS]
995 << "filetools(getExtFromContents)\n"
996 << "\tFile type not recognised before EOF!"
1002 string const stamp = str.substr(0,2);
1003 if (firstLine && str.size() >= 2) {
1004 // at first we check for a zipped file, because this
1005 // information is saved in the first bytes of the file!
1006 // also some graphic formats which save the information
1007 // in the first line, too.
1008 if (prefixIs(str, gzipStamp)) {
1011 } else if (stamp == zipStamp) {
1014 } else if (stamp == compressStamp) {
1015 format = "compress";
1017 // the graphics part
1018 } else if (stamp == "BM") {
1021 } else if (stamp == "\001\332") {
1025 // Don't need to use str.at(0), str.at(1) because
1026 // we already know that str.size() >= 2
1027 } else if (str[0] == 'P') {
1043 } else if ((stamp == "II") || (stamp == "MM")) {
1046 } else if (prefixIs(str,"%TGIF")) {
1049 } else if (prefixIs(str,"#FIG")) {
1052 } else if (prefixIs(str,"GIF")) {
1055 } else if (str.size() > 3) {
1056 int const c = ((str[0] << 24) & (str[1] << 16) &
1057 (str[2] << 8) & str[3]);
1066 if (!format.empty())
1068 else if (contains(str,"EPSF"))
1069 // dummy, if we have wrong file description like
1070 // %!PS-Adobe-2.0EPSF"
1073 else if (contains(str,"Grace"))
1076 else if (contains(str,"JFIF"))
1079 else if (contains(str,"%PDF"))
1082 else if (contains(str,"PNG"))
1085 else if (contains(str,"%!PS-Adobe")) {
1088 if (contains(str,"EPSF"))
1094 else if (contains(str,"_bits[]"))
1097 else if (contains(str,"XPM") || contains(str, "static char *"))
1100 else if (contains(str,"BITPIX"))
1104 if (!format.empty()) {
1105 lyxerr[Debug::GRAPHICS]
1106 << "Recognised Fileformat: " << format << endl;
1110 string const ext(GetExtension(filename));
1111 lyxerr[Debug::GRAPHICS]
1112 << "filetools(getExtFromContents)\n"
1113 << "\tCouldn't find a known Type!\n";
1115 lyxerr[Debug::GRAPHICS]
1116 << "\twill take the file extension -> "
1120 lyxerr[Debug::GRAPHICS]
1121 << "\twill use ext or a \"user\" defined format" << endl;
1127 /// check for zipped file
1128 bool zippedFile(string const & name)
1130 string const type = getExtFromContents(name);
1131 if (contains("gzip zip compress", type) && !type.empty())
1137 string const unzipFile(string const & zipped_file)
1139 string const file = ChangeExtension(zipped_file, string());
1140 string const tempfile = lyx::tempName(string(), file);
1142 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1144 one.startscript(Systemcall::Wait, command);
1145 // test that command was executed successfully (anon)
1146 // yes, please do. (Lgb)
1151 string const MakeDisplayPath(string const & path, unsigned int threshold)
1155 string const home(GetEnvPath("HOME"));
1157 // replace /home/blah with ~/
1158 if (prefixIs(str, home))
1159 str = subst(str, home, "~");
1161 if (str.length() <= threshold)
1164 string const prefix = ".../";
1167 while (str.length() > threshold)
1168 str = split(str, temp, '/');
1170 // Did we shorten everything away?
1172 // Yes, filename itself is too long.
1173 // Pick the start and the end of the filename.
1174 str = OnlyFilename(path);
1175 string const head = str.substr(0, threshold / 2 - 3);
1177 string::size_type len = str.length();
1179 str.substr(len - threshold / 2 - 2, len - 1);
1180 str = head + "..." + tail;
1183 return prefix + str;
1187 bool LyXReadLink(string const & file, string & link, bool resolve)
1189 char linkbuffer[512];
1190 // Should be PATH_MAX but that needs autconf support
1191 int const nRead = ::readlink(file.c_str(),
1192 linkbuffer, sizeof(linkbuffer) - 1);
1195 linkbuffer[nRead] = '\0'; // terminator
1197 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1204 cmd_ret const RunCommand(string const & cmd)
1206 // One question is if we should use popen or
1207 // create our own popen based on fork, exec, pipe
1208 // of course the best would be to have a
1209 // pstream (process stream), with the
1210 // variants ipstream, opstream
1212 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1214 // (Claus Hentschel) Check if popen was succesful ;-)
1216 return make_pair(-1, string());
1221 ret += static_cast<char>(c);
1224 int const pret = pclose(inf);
1225 return make_pair(pret, ret);
1229 string const findtexfile(string const & fil, string const & /*format*/)
1231 /* There is no problem to extend this function too use other
1232 methods to look for files. It could be setup to look
1233 in environment paths and also if wanted as a last resort
1234 to a recursive find. One of the easier extensions would
1235 perhaps be to use the LyX file lookup methods. But! I am
1236 going to implement this until I see some demand for it.
1240 // If the file can be found directly, we just return a
1241 // absolute path version of it.
1242 if (FileInfo(fil).exist())
1243 return MakeAbsPath(fil);
1245 // No we try to find it using kpsewhich.
1246 // It seems from the kpsewhich manual page that it is safe to use
1247 // kpsewhich without --format: "When the --format option is not
1248 // given, the search path used when looking for a file is inferred
1249 // from the name given, by looking for a known extension. If no
1250 // known extension is found, the search path for TeX source files
1252 // However, we want to take advantage of the format sine almost all
1253 // the different formats has environment variables that can be used
1254 // to controll which paths to search. f.ex. bib looks in
1255 // BIBINPUTS and TEXBIB. Small list follows:
1256 // bib - BIBINPUTS, TEXBIB
1258 // graphic/figure - TEXPICTS, TEXINPUTS
1259 // ist - TEXINDEXSTYLE, INDEXSTYLE
1260 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1262 // tfm - TFMFONTS, TEXFONTS
1263 // This means that to use kpsewhich in the best possible way we
1264 // should help it by setting additional path in the approp. envir.var.
1265 string const kpsecmd = "kpsewhich " + fil;
1267 cmd_ret const c = RunCommand(kpsecmd);
1269 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1270 << "kpse result = `" << rtrim(c.second, "\n")
1273 return os::internal_path(rtrim(c.second, "\n\r"));
1279 void removeAutosaveFile(string const & filename)
1281 string a = OnlyPath(filename);
1283 a += OnlyFilename(filename);
1285 FileInfo const fileinfo(a);
1286 if (fileinfo.exist())
1291 void readBB_lyxerrMessage(string const & file, bool & zipped,
1292 string const & message)
1294 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1295 << message << std::endl;
1301 string const readBB_from_PSFile(string const & file)
1303 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1304 // It seems that every command in the header has an own line,
1305 // getline() should work for all files.
1306 // On the other hand some plot programs write the bb at the
1307 // end of the file. Than we have in the header:
1308 // %%BoundingBox: (atend)
1309 // In this case we must check the end.
1310 bool zipped = zippedFile(file);
1311 string const file_ = zipped ?
1312 string(unzipFile(file)) : string(file);
1313 string const format = getExtFromContents(file_);
1315 if (format != "eps" && format != "ps") {
1316 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1320 std::ifstream is(file_.c_str());
1324 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1325 string const bb = ltrim(s.substr(14));
1326 readBB_lyxerrMessage(file_, zipped, bb);
1330 readBB_lyxerrMessage(file_, zipped, "no bb found");
1335 string const copyFileToDir(string const & path, string const & file_in)
1337 lyx::Assert(AbsolutePath(path));
1339 // First, make the file path relative to path.
1340 string file_out = MakeRelPath(path, NormalizePath(file_in));
1341 file_out = os::slashify_path(file_out);
1343 // Now generate a unique filename.
1344 // Remove the extension.
1345 file_out = ChangeExtension(file_out, string());
1346 // Replace '/' in the file name with '_'
1347 file_out = subst(file_out, "/", "_");
1348 // Replace '.' in the file name with '_'
1349 file_out = subst(file_out, ".", "_");
1350 // Append a unique ID
1352 file_out += '_' + tostr(id++);
1353 // Add the extension back on
1354 file_out = ChangeExtension(file_out, GetExtension(file_in));
1355 // Put this file in the buffer's temp dir
1356 file_out = MakeAbsPath(file_out, path);
1358 // If the original is newer than the copy, then copy the original
1359 // to the new directory.
1360 FileInfo fi(file_in);
1361 FileInfo fi2(file_out);
1363 bool success = true;
1366 difftime(fi.getModificationTime(),
1367 fi2.getModificationTime()) >= 0)
1368 success = lyx::copy(file_in, file_out);
1371 return success ? file_out : string();