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;
89 bool IsLyXFilename(string const & filename)
91 return suffixIs(ascii_lowercase(filename), ".lyx");
95 bool IsSGMLFilename(string const & filename)
97 return suffixIs(ascii_lowercase(filename), ".sgml");
101 // Substitutes spaces with underscores in filename (and path)
102 string const MakeLatexName(string const & file)
104 string name = OnlyFilename(file);
105 string const path = OnlyPath(file);
107 for (string::size_type i = 0; i < name.length(); ++i) {
108 name[i] &= 0x7f; // set 8th bit to 0
111 // ok so we scan through the string twice, but who cares.
112 string const keep("abcdefghijklmnopqrstuvwxyz"
113 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
114 "@!\"'()*+,-./0123456789:;<=>?[]`|");
116 string::size_type pos = 0;
117 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
120 return AddName(path, name);
124 // Substitutes spaces with underscores in filename (and path)
125 string const QuoteName(string const & name)
127 return (os::shell() == os::UNIX) ?
133 // Is a file readable ?
134 bool IsFileReadable(string const & path)
137 return file.isOK() && file.isRegular() && file.readable();
141 // Is a file read_only?
142 // return 1 read-write
144 // -1 error (doesn't exist, no access, anything else)
145 int IsFileWriteable(string const & path)
149 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
151 if (fi.readable()) // read-only
153 return -1; // everything else.
157 //returns true: dir writeable
158 // false: not writeable
159 bool IsDirWriteable(string const & path)
161 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
163 string const tmpfl(tempName(path, "lyxwritetest"));
173 // Uses a string of paths separated by ";"s to find a file to open.
174 // Can't cope with pathnames with a ';' in them. Returns full path to file.
175 // If path entry begins with $$LyX/, use system_lyxdir
176 // If path entry begins with $$User/, use user_lyxdir
177 // Example: "$$User/doc;$$LyX/doc"
178 string const FileOpenSearch(string const & path, string const & name,
183 bool notfound = true;
184 string tmppath = split(path, path_element, ';');
186 while (notfound && !path_element.empty()) {
187 path_element = os::slashify_path(path_element);
188 if (!suffixIs(path_element, '/'))
190 path_element = subst(path_element, "$$LyX", system_lyxdir);
191 path_element = subst(path_element, "$$User", user_lyxdir);
193 real_file = FileSearch(path_element, name, ext);
195 if (real_file.empty()) {
197 tmppath = split(tmppath, path_element, ';');
198 } while (!tmppath.empty() && path_element.empty());
204 if (ext.empty() && notfound) {
205 real_file = FileOpenSearch(path, name, "exe");
206 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
213 /// Returns a vector of all files in directory dir having extension ext.
214 vector<string> const DirList(string const & dir, string const & ext)
216 // This is a non-error checking C/system implementation
218 if (!ext.empty() && ext[0] != '.')
222 vector<string> dirlist;
223 DIR * dirp = ::opendir(dir.c_str());
226 << "Directory \"" << dir
227 << "\" does not exist to DirList." << endl;
232 while ((dire = ::readdir(dirp))) {
233 string const fil = dire->d_name;
234 if (suffixIs(fil, extension)) {
235 dirlist.push_back(fil);
240 /* I would have prefered to take a vector<string>& as parameter so
241 that we could avoid the copy of the vector when returning.
243 dirlist.swap(argvec);
244 to avoid the copy. (Lgb)
246 /* A C++ implementaion will look like this:
247 string extension(ext);
248 if (extension[0] != '.') extension.insert(0, 1, '.');
249 vector<string> dirlist;
250 directory_iterator dit("dir");
251 while (dit != directory_iterator()) {
252 string fil = dit->filename;
253 if (prefixIs(fil, extension)) {
254 dirlist.push_back(fil);
258 dirlist.swap(argvec);
264 // Returns the real name of file name in directory path, with optional
266 string const FileSearch(string const & path, string const & name,
269 // if `name' is an absolute path, we ignore the setting of `path'
270 // Expand Environmentvariables in 'name'
271 string const tmpname = ReplaceEnvironmentPath(name);
272 string fullname = MakeAbsPath(tmpname, path);
273 // search first without extension, then with it.
274 if (IsFileReadable(fullname))
276 else if (ext.empty())
278 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
281 if (IsFileReadable(fullname))
289 // Search the file name.ext in the subdirectory dir of
291 // 2) build_lyxdir (if not empty)
293 string const LibFileSearch(string const & dir, string const & name,
296 string fullname = FileSearch(AddPath(user_lyxdir, dir), name, ext);
297 if (!fullname.empty())
300 if (!build_lyxdir.empty())
301 fullname = FileSearch(AddPath(build_lyxdir, dir), name, ext);
302 if (!fullname.empty())
305 return FileSearch(AddPath(system_lyxdir, dir), name, ext);
310 i18nLibFileSearch(string const & dir, string const & name,
313 // this comment is from intl/dcigettext.c. We try to mimick this
315 /* The highest priority value is the `LANGUAGE' environment
316 variable. But we don't use the value if the currently
317 selected locale is the C locale. This is a GNU extension. */
319 string const lc_all = GetEnv("LC_ALL");
320 string lang = GetEnv("LANGUAGE");
321 if (lang.empty() || lc_all == "C") {
324 lang = GetEnv("LANG");
328 lang = token(lang, '_', 0);
330 if (lang.empty() || lang == "C")
331 return LibFileSearch(dir, name, ext);
333 string const tmp = LibFileSearch(dir, lang + '_' + name,
338 return LibFileSearch(dir, name, ext);
343 string const LibScriptSearch(string const & command_in)
345 string const token_scriptpath("$$s/");
347 string command = command_in;
348 // Find the starting position of "$$s/"
349 string::size_type const pos1 = command.find(token_scriptpath);
350 if (pos1 == string::npos)
352 // Find the end of the "$$s/some_script" word within command
353 string::size_type const start_script = pos1 + 4;
354 string::size_type const pos2 = command.find(' ', start_script);
355 string::size_type const size_script = pos2 == string::npos?
356 (command.size() - start_script) : pos2 - start_script;
358 // Does this script file exist?
359 string const script =
360 LibFileSearch("scripts", command.substr(start_script, size_script));
362 if (script.empty()) {
363 // Replace "$$s/" with ""
364 command.erase(pos1, 4);
366 // Replace "$$s/some_script" with "$LYX_SCRIPT_PATH/some_script"
367 string::size_type const size_replace = size_script + 4;
368 command.replace(pos1, size_replace, script);
375 string const GetEnv(string const & envname)
377 // f.ex. what about error checking?
378 char const * const ch = getenv(envname.c_str());
379 string const envstr = !ch ? "" : ch;
384 string const GetEnvPath(string const & name)
387 string const pathlist = subst(GetEnv(name), ':', ';');
389 string const pathlist = os::slashify_path(GetEnv(name));
391 return rtrim(pathlist, ";");
397 int DeleteAllFilesInDir(string const & path)
399 // I have decided that we will be using parts from the boost
400 // library. Check out http://www.boost.org/
401 // For directory access we will then use the directory_iterator.
402 // Then the code will be something like:
403 // directory_iterator dit(path);
404 // directory_iterator dend;
405 // if (dit == dend) {
408 // for (; dit != dend; ++dit) {
409 // string filename(*dit);
410 // if (filename == "." || filename == "..")
412 // string unlinkpath(AddName(path, filename));
413 // lyx::unlink(unlinkpath);
416 DIR * dir = ::opendir(path.c_str());
421 int return_value = 0;
422 while ((de = readdir(dir))) {
423 string const temp = de->d_name;
424 if (temp == "." || temp == "..")
426 string const unlinkpath = AddName (path, temp);
428 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
432 FileInfo fi(unlinkpath);
433 if (fi.isOK() && fi.isDir())
434 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
435 deleted &= (unlink(unlinkpath) == 0);
444 string const CreateTmpDir(string const & tempdir, string const & mask)
447 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
448 << "CreateTmpDir: mask=`" << mask << '\'' << endl;
450 string const tmpfl(tempName(tempdir, mask));
451 // lyx::tempName actually creates a file to make sure that it
452 // stays unique. So we have to delete it before we can create
453 // a dir with the same name. Note also that we are not thread
454 // safe because of the gap between unlink and mkdir. (Lgb)
457 if (tmpfl.empty() || mkdir(tmpfl, 0700))
460 return MakeAbsPath(tmpfl);
466 int destroyDir(string const & tmpdir)
471 if (DeleteAllFilesInDir(tmpdir))
481 string const CreateBufferTmpDir(string const & pathfor)
484 static string const tmpdir(pathfor.empty() ? os::getTmpDir() : pathfor);
485 // We are in our own directory. Why bother to mangle name?
486 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
488 string const tmpfl = tmpdir + "/lyx_tmpbuf" + tostr(count++);
489 if (mkdir(tmpfl, 0777)) {
496 string const CreateLyXTmpDir(string const & deflt)
498 if ((!deflt.empty()) && (deflt != "/tmp")) {
499 if (mkdir(deflt, 0777)) {
503 return CreateTmpDir(deflt, "lyx_tmpdir");
510 return CreateTmpDir("/tmp", "lyx_tmpdir");
515 bool createDirectory(string const & path, int permission)
517 string temp(rtrim(os::slashify_path(path), "/"));
519 Assert(!temp.empty());
521 if (mkdir(temp, permission))
528 // Strip filename from path name
529 string const OnlyPath(string const & Filename)
531 // If empty filename, return empty
532 if (Filename.empty()) return Filename;
534 // Find last / or start of filename
535 string::size_type j = Filename.rfind('/');
536 if (j == string::npos)
538 return Filename.substr(0, j + 1);
542 // Convert relative path into absolute path based on a basepath.
543 // If relpath is absolute, just use that.
544 // If basepath is empty, use CWD as base.
545 string const MakeAbsPath(string const & RelPath, string const & BasePath)
547 // checks for already absolute path
548 if (os::is_absolute_path(RelPath))
551 // Copies given paths
552 string TempRel(os::slashify_path(RelPath));
553 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
554 TempRel = subst(TempRel, "//", "/");
558 if (os::is_absolute_path(BasePath))
561 TempBase = AddPath(getcwd(), BasePath);
563 // Handle /./ at the end of the path
564 while (suffixIs(TempBase, "/./"))
565 TempBase.erase(TempBase.length() - 2);
567 // processes relative path
568 string RTemp(TempRel);
571 while (!RTemp.empty()) {
573 RTemp = split(RTemp, Temp, '/');
575 if (Temp == ".") continue;
577 // Remove one level of TempBase
578 string::difference_type i = TempBase.length() - 2;
581 while (i > 0 && TempBase[i] != '/') --i;
585 while (i > 2 && TempBase[i] != '/') --i;
588 TempBase.erase(i, string::npos);
591 } else if (Temp.empty() && !RTemp.empty()) {
592 TempBase = os::current_root() + RTemp;
595 // Add this piece to TempBase
596 if (!suffixIs(TempBase, '/'))
602 // returns absolute path
603 return os::slashify_path(TempBase);
607 // Correctly append filename to the pathname.
608 // If pathname is '.', then don't use pathname.
609 // Chops any path of filename.
610 string const AddName(string const & path, string const & fname)
613 string const basename(OnlyFilename(fname));
617 if (path != "." && path != "./" && !path.empty()) {
618 buf = os::slashify_path(path);
619 if (!suffixIs(path, '/'))
623 return buf + basename;
627 // Strips path from filename
628 string const OnlyFilename(string const & fname)
633 string::size_type j = fname.rfind('/');
634 if (j == string::npos) // no '/' in fname
638 return fname.substr(j + 1);
642 /// Returns true is path is absolute
643 bool AbsolutePath(string const & path)
645 return os::is_absolute_path(path);
650 // Create absolute path. If impossible, don't do anything
651 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
652 string const ExpandPath(string const & path)
654 // checks for already absolute path
655 string RTemp(ReplaceEnvironmentPath(path));
656 if (os::is_absolute_path(RTemp))
660 string const copy(RTemp);
663 RTemp = split(RTemp, Temp, '/');
666 return getcwd() + '/' + RTemp;
669 return GetEnvPath("HOME") + '/' + RTemp;
672 return MakeAbsPath(copy);
674 // Don't know how to handle this
680 // Constracts path/../path
681 // Can't handle "../../" or "/../" (Asger)
682 // Also converts paths like /foo//bar ==> /foo/bar
683 string const NormalizePath(string const & path)
689 if (os::is_absolute_path(path))
692 // Make implicit current directory explicit
695 // Normalise paths like /foo//bar ==> /foo/bar
696 boost::RegEx regex("/{2,}");
697 RTemp = STRCONV(regex.Merge(STRCONV(RTemp), "/"));
699 while (!RTemp.empty()) {
701 RTemp = split(RTemp, Temp, '/');
705 } else if (Temp == "..") {
706 // Remove one level of TempBase
707 string::difference_type i = TempBase.length() - 2;
708 while (i > 0 && TempBase[i] != '/')
710 if (i >= 0 && TempBase[i] == '/')
711 TempBase.erase(i + 1, string::npos);
715 TempBase += Temp + '/';
719 // returns absolute path
724 string const GetFileContents(string const & fname)
726 FileInfo finfo(fname);
728 ifstream ifs(fname.c_str());
733 return STRCONV(ofs.str());
736 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
742 // Search ${...} as Variable-Name inside the string and replace it with
743 // the denoted environmentvariable
744 // Allow Variables according to
745 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
748 string const ReplaceEnvironmentPath(string const & path)
751 // CompareChar: Environment variables starts with this character
752 // PathChar: Next path component start with this character
753 // while CompareChar found do:
754 // Split String with PathChar
755 // Search Environmentvariable
756 // if found: Replace Strings
758 char const CompareChar = '$';
759 char const FirstChar = '{';
760 char const EndChar = '}';
761 char const UnderscoreChar = '_';
762 string EndString; EndString += EndChar;
763 string FirstString; FirstString += FirstChar;
764 string CompareString; CompareString += CompareChar;
765 string const RegExp("*}*"); // Exist EndChar inside a String?
767 // first: Search for a '$' - Sign.
769 string result1; //(copy); // for split-calls
770 string result0 = split(path, result1, CompareChar);
771 while (!result0.empty()) {
772 string copy1(result0); // contains String after $
774 // Check, if there is an EndChar inside original String.
776 if (!regexMatch(copy1, RegExp)) {
777 // No EndChar inside. So we are finished
778 result1 += CompareString + result0;
784 string res0 = split(copy1, res1, EndChar);
785 // Now res1 holds the environmentvariable
786 // First, check, if Contents is ok.
787 if (res1.empty()) { // No environmentvariable. Continue Loop.
788 result1 += CompareString + FirstString;
792 // check contents of res1
793 char const * res1_contents = res1.c_str();
794 if (*res1_contents != FirstChar) {
795 // Again No Environmentvariable
796 result1 += CompareString;
800 // Check for variable names
801 // Situation ${} is detected as "No Environmentvariable"
802 char const * cp1 = res1_contents + 1;
803 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
805 while (*cp1 && result) {
806 result = isalnum(*cp1) ||
807 (*cp1 == UnderscoreChar);
812 // no correct variable name
813 result1 += CompareString + res1 + EndString;
814 result0 = split(res0, res1, CompareChar);
819 string env(GetEnv(res1_contents + 1));
821 // Congratulations. Environmentvariable found
824 result1 += CompareString + res1 + EndString;
827 result0 = split(res0, res1, CompareChar);
834 // Make relative path out of two absolute paths
835 string const MakeRelPath(string const & abspath, string const & basepath)
836 // Makes relative path out of absolute path. If it is deeper than basepath,
837 // it's easy. If basepath and abspath share something (they are all deeper
838 // than some directory), it'll be rendered using ..'s. If they are completely
839 // different, then the absolute path will be used as relative path.
841 string::size_type const abslen = abspath.length();
842 string::size_type const baselen = basepath.length();
844 string::size_type i = os::common_path(abspath, basepath);
847 // actually no match - cannot make it relative
851 // Count how many dirs there are in basepath above match
852 // and append as many '..''s into relpath
854 string::size_type j = i;
855 while (j < baselen) {
856 if (basepath[j] == '/') {
857 if (j + 1 == baselen)
864 // Append relative stuff from common directory to abspath
865 if (abspath[i] == '/')
867 for (; i < abslen; ++i)
870 if (suffixIs(buf, '/'))
871 buf.erase(buf.length() - 1);
872 // Substitute empty with .
879 // Append sub-directory(ies) to a path in an intelligent way
880 string const AddPath(string const & path, string const & path_2)
883 string const path2 = os::slashify_path(path_2);
885 if (!path.empty() && path != "." && path != "./") {
886 buf = os::slashify_path(path);
887 if (path[path.length() - 1] != '/')
891 if (!path2.empty()) {
892 string::size_type const p2start = path2.find_first_not_of('/');
893 string::size_type const p2end = path2.find_last_not_of('/');
894 string const tmp = path2.substr(p2start, p2end - p2start + 1);
902 Change extension of oldname to extension.
903 Strips path off if no_path == true.
904 If no extension on oldname, just appends.
906 string const ChangeExtension(string const & oldname, string const & extension)
908 string::size_type const last_slash = oldname.rfind('/');
909 string::size_type last_dot = oldname.rfind('.');
910 if (last_dot < last_slash && last_slash != string::npos)
911 last_dot = string::npos;
914 // Make sure the extension starts with a dot
915 if (!extension.empty() && extension[0] != '.')
916 ext= '.' + extension;
920 return os::slashify_path(oldname.substr(0, last_dot) + ext);
924 /// Return the extension of the file (not including the .)
925 string const GetExtension(string const & name)
927 string::size_type const last_slash = name.rfind('/');
928 string::size_type const last_dot = name.rfind('.');
929 if (last_dot != string::npos &&
930 (last_slash == string::npos || last_dot > last_slash))
931 return name.substr(last_dot + 1,
932 name.length() - (last_dot + 1));
937 // the different filetypes and what they contain in one of the first lines
938 // (dots are any characters). (Herbert 20020131)
941 // EPS %!PS-Adobe-3.0 EPSF...
948 // PBM P1... or P4 (B/W)
949 // PGM P2... or P5 (Grayscale)
950 // PPM P3... or P6 (color)
951 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
952 // SGI \001\332... (decimal 474)
954 // TIFF II... or MM...
956 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
957 // ...static char *...
958 // XWD \000\000\000\151 (0x00006900) decimal 105
960 // GZIP \037\213\010\010... http://www.ietf.org/rfc/rfc1952.txt
961 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
962 // Z \037\177 UNIX compress
964 /// return the "extension" which belongs to the contents.
965 /// for no knowing contents return the extension. Without
966 /// an extension and unknown contents we return "user"
967 string const getExtFromContents(string const & filename)
970 if (filename.empty() || !IsFileReadable(filename))
974 ifstream ifs(filename.c_str());
976 // Couldn't open file...
980 string const gzipStamp = "\037\213\010\010";
983 string const zipStamp = "PK";
986 string const compressStamp = "\037\177";
988 // Maximum strings to read
989 int const max_count = 50;
993 bool firstLine = true;
994 while ((count++ < max_count) && format.empty()) {
996 lyxerr[Debug::GRAPHICS]
997 << "filetools(getExtFromContents)\n"
998 << "\tFile type not recognised before EOF!"
1004 string const stamp = str.substr(0,2);
1005 if (firstLine && str.size() >= 2) {
1006 // at first we check for a zipped file, because this
1007 // information is saved in the first bytes of the file!
1008 // also some graphic formats which save the information
1009 // in the first line, too.
1010 if (prefixIs(str, gzipStamp)) {
1013 } else if (stamp == zipStamp) {
1016 } else if (stamp == compressStamp) {
1017 format = "compress";
1019 // the graphics part
1020 } else if (stamp == "BM") {
1023 } else if (stamp == "\001\332") {
1027 // Don't need to use str.at(0), str.at(1) because
1028 // we already know that str.size() >= 2
1029 } else if (str[0] == 'P') {
1045 } else if ((stamp == "II") || (stamp == "MM")) {
1048 } else if (prefixIs(str,"%TGIF")) {
1051 } else if (prefixIs(str,"#FIG")) {
1054 } else if (prefixIs(str,"GIF")) {
1057 } else if (str.size() > 3) {
1058 int const c = ((str[0] << 24) & (str[1] << 16) &
1059 (str[2] << 8) & str[3]);
1068 if (!format.empty())
1070 else if (contains(str,"EPSF"))
1071 // dummy, if we have wrong file description like
1072 // %!PS-Adobe-2.0EPSF"
1075 else if (contains(str,"Grace"))
1078 else if (contains(str,"JFIF"))
1081 else if (contains(str,"%PDF"))
1084 else if (contains(str,"PNG"))
1087 else if (contains(str,"%!PS-Adobe")) {
1090 if (contains(str,"EPSF"))
1096 else if (contains(str,"_bits[]"))
1099 else if (contains(str,"XPM") || contains(str, "static char *"))
1102 else if (contains(str,"BITPIX"))
1106 if (!format.empty()) {
1107 lyxerr[Debug::GRAPHICS]
1108 << "Recognised Fileformat: " << format << endl;
1112 string const ext(GetExtension(filename));
1113 lyxerr[Debug::GRAPHICS]
1114 << "filetools(getExtFromContents)\n"
1115 << "\tCouldn't find a known Type!\n";
1117 lyxerr[Debug::GRAPHICS]
1118 << "\twill take the file extension -> "
1122 lyxerr[Debug::GRAPHICS]
1123 << "\twill use ext or a \"user\" defined format" << endl;
1129 /// check for zipped file
1130 bool zippedFile(string const & name)
1132 string const type = getExtFromContents(name);
1133 if (contains("gzip zip compress", type) && !type.empty())
1139 string const unzipFile(string const & zipped_file)
1141 string const file = ChangeExtension(zipped_file, string());
1142 string const tempfile = tempName(string(), file);
1144 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1146 one.startscript(Systemcall::Wait, command);
1147 // test that command was executed successfully (anon)
1148 // yes, please do. (Lgb)
1153 string const MakeDisplayPath(string const & path, unsigned int threshold)
1157 string const home(GetEnvPath("HOME"));
1159 // replace /home/blah with ~/
1160 if (prefixIs(str, home))
1161 str = subst(str, home, "~");
1163 if (str.length() <= threshold)
1166 string const prefix = ".../";
1169 while (str.length() > threshold)
1170 str = split(str, temp, '/');
1172 // Did we shorten everything away?
1174 // Yes, filename itself is too long.
1175 // Pick the start and the end of the filename.
1176 str = OnlyFilename(path);
1177 string const head = str.substr(0, threshold / 2 - 3);
1179 string::size_type len = str.length();
1181 str.substr(len - threshold / 2 - 2, len - 1);
1182 str = head + "..." + tail;
1185 return prefix + str;
1189 bool LyXReadLink(string const & file, string & link, bool resolve)
1191 char linkbuffer[512];
1192 // Should be PATH_MAX but that needs autconf support
1193 int const nRead = ::readlink(file.c_str(),
1194 linkbuffer, sizeof(linkbuffer) - 1);
1197 linkbuffer[nRead] = '\0'; // terminator
1199 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1206 cmd_ret const RunCommand(string const & cmd)
1208 // One question is if we should use popen or
1209 // create our own popen based on fork, exec, pipe
1210 // of course the best would be to have a
1211 // pstream (process stream), with the
1212 // variants ipstream, opstream
1214 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1216 // (Claus Hentschel) Check if popen was succesful ;-)
1218 return make_pair(-1, string());
1223 ret += static_cast<char>(c);
1226 int const pret = pclose(inf);
1227 return make_pair(pret, ret);
1231 string const findtexfile(string const & fil, string const & /*format*/)
1233 /* There is no problem to extend this function too use other
1234 methods to look for files. It could be setup to look
1235 in environment paths and also if wanted as a last resort
1236 to a recursive find. One of the easier extensions would
1237 perhaps be to use the LyX file lookup methods. But! I am
1238 going to implement this until I see some demand for it.
1242 // If the file can be found directly, we just return a
1243 // absolute path version of it.
1244 if (FileInfo(fil).exist())
1245 return MakeAbsPath(fil);
1247 // No we try to find it using kpsewhich.
1248 // It seems from the kpsewhich manual page that it is safe to use
1249 // kpsewhich without --format: "When the --format option is not
1250 // given, the search path used when looking for a file is inferred
1251 // from the name given, by looking for a known extension. If no
1252 // known extension is found, the search path for TeX source files
1254 // However, we want to take advantage of the format sine almost all
1255 // the different formats has environment variables that can be used
1256 // to controll which paths to search. f.ex. bib looks in
1257 // BIBINPUTS and TEXBIB. Small list follows:
1258 // bib - BIBINPUTS, TEXBIB
1260 // graphic/figure - TEXPICTS, TEXINPUTS
1261 // ist - TEXINDEXSTYLE, INDEXSTYLE
1262 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1264 // tfm - TFMFONTS, TEXFONTS
1265 // This means that to use kpsewhich in the best possible way we
1266 // should help it by setting additional path in the approp. envir.var.
1267 string const kpsecmd = "kpsewhich " + fil;
1269 cmd_ret const c = RunCommand(kpsecmd);
1271 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1272 << "kpse result = `" << rtrim(c.second, "\n")
1275 return os::internal_path(rtrim(c.second, "\n\r"));
1281 void removeAutosaveFile(string const & filename)
1283 string a = OnlyPath(filename);
1285 a += OnlyFilename(filename);
1287 FileInfo const fileinfo(a);
1288 if (fileinfo.exist())
1293 void readBB_lyxerrMessage(string const & file, bool & zipped,
1294 string const & message)
1296 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1297 << message << std::endl;
1298 #warning Why is this func deleting a file? (Lgb)
1304 string const readBB_from_PSFile(string const & file)
1306 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1307 // It seems that every command in the header has an own line,
1308 // getline() should work for all files.
1309 // On the other hand some plot programs write the bb at the
1310 // end of the file. Than we have in the header:
1311 // %%BoundingBox: (atend)
1312 // In this case we must check the end.
1313 bool zipped = zippedFile(file);
1314 string const file_ = zipped ?
1315 string(unzipFile(file)) : string(file);
1316 string const format = getExtFromContents(file_);
1318 if (format != "eps" && format != "ps") {
1319 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1323 std::ifstream is(file_.c_str());
1327 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1328 string const bb = ltrim(s.substr(14));
1329 readBB_lyxerrMessage(file_, zipped, bb);
1333 readBB_lyxerrMessage(file_, zipped, "no bb found");
1338 string const copyFileToDir(string const & path, string const & file_in)
1340 Assert(AbsolutePath(path));
1342 // First, make the file path relative to path.
1343 string file_out = MakeRelPath(path, NormalizePath(file_in));
1344 file_out = os::slashify_path(file_out);
1346 // Now generate a unique filename.
1347 // Remove the extension.
1348 file_out = ChangeExtension(file_out, string());
1349 // Replace '/' in the file name with '_'
1350 file_out = subst(file_out, "/", "_");
1351 // Replace '.' in the file name with '_'
1352 file_out = subst(file_out, ".", "_");
1353 // Append a unique ID
1355 file_out += '_' + tostr(id++);
1356 // Add the extension back on
1357 file_out = ChangeExtension(file_out, GetExtension(file_in));
1358 // Put this file in the buffer's temp dir
1359 file_out = MakeAbsPath(file_out, path);
1361 // If the original is newer than the copy, then copy the original
1362 // to the new directory.
1363 FileInfo fi(file_in);
1364 FileInfo fi2(file_out);
1366 bool success = true;
1369 difftime(fi.getModificationTime(),
1370 fi2.getModificationTime()) >= 0)
1371 success = copy(file_in, file_out);
1374 return success ? file_out : string();
1377 } //namespace support