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"
34 #include "path_defines.h"
41 #include <boost/cregex.hpp>
52 // Which part of this is still necessary? (JMarc).
55 # define NAMLEN(dirent) strlen((dirent)->d_name)
57 # define dirent direct
58 # define NAMLEN(dirent) (dirent)->d_namlen
60 # include <sys/ndir.h>
70 #ifndef CXX_GLOBAL_CSTD
83 extern string user_lyxdir;
88 bool IsLyXFilename(string const & filename)
90 return suffixIs(ascii_lowercase(filename), ".lyx");
94 bool IsSGMLFilename(string const & filename)
96 return suffixIs(ascii_lowercase(filename), ".sgml");
100 // Substitutes spaces with underscores in filename (and path)
101 string const MakeLatexName(string const & file)
103 string name = OnlyFilename(file);
104 string const path = OnlyPath(file);
106 for (string::size_type i = 0; i < name.length(); ++i) {
107 name[i] &= 0x7f; // set 8th bit to 0
110 // ok so we scan through the string twice, but who cares.
111 string const keep("abcdefghijklmnopqrstuvwxyz"
112 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
113 "@!\"'()*+,-./0123456789:;<=>?[]`|");
115 string::size_type pos = 0;
116 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
119 return AddName(path, name);
123 // Substitutes spaces with underscores in filename (and path)
124 string const QuoteName(string const & name)
126 return (os::shell() == os::UNIX) ?
132 // Is a file readable ?
133 bool IsFileReadable(string const & path)
136 return file.isOK() && file.isRegular() && file.readable();
140 // Is a file read_only?
141 // return 1 read-write
143 // -1 error (doesn't exist, no access, anything else)
144 int IsFileWriteable(string const & path)
148 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
150 if (fi.readable()) // read-only
152 return -1; // everything else.
156 //returns true: dir writeable
157 // false: not writeable
158 bool IsDirWriteable(string const & path)
160 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
162 string const tmpfl(tempName(path, "lyxwritetest"));
172 // Uses a string of paths separated by ";"s to find a file to open.
173 // Can't cope with pathnames with a ';' in them. Returns full path to file.
174 // If path entry begins with $$LyX/, use system_lyxdir
175 // If path entry begins with $$User/, use user_lyxdir
176 // Example: "$$User/doc;$$LyX/doc"
177 string const FileOpenSearch(string const & path, string const & name,
182 bool notfound = true;
183 string tmppath = split(path, path_element, ';');
185 while (notfound && !path_element.empty()) {
186 path_element = os::slashify_path(path_element);
187 if (!suffixIs(path_element, '/'))
189 path_element = subst(path_element, "$$LyX", system_lyxdir());
190 path_element = subst(path_element, "$$User", user_lyxdir);
192 real_file = FileSearch(path_element, name, ext);
194 if (real_file.empty()) {
196 tmppath = split(tmppath, path_element, ';');
197 } while (!tmppath.empty() && path_element.empty());
203 if (ext.empty() && notfound) {
204 real_file = FileOpenSearch(path, name, "exe");
205 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
212 /// Returns a vector of all files in directory dir having extension ext.
213 vector<string> const DirList(string const & dir, string const & ext)
215 // This is a non-error checking C/system implementation
217 if (!ext.empty() && ext[0] != '.')
221 vector<string> dirlist;
222 DIR * dirp = ::opendir(dir.c_str());
225 << "Directory \"" << dir
226 << "\" does not exist to DirList." << endl;
231 while ((dire = ::readdir(dirp))) {
232 string const fil = dire->d_name;
233 if (suffixIs(fil, extension)) {
234 dirlist.push_back(fil);
239 /* I would have prefered to take a vector<string>& as parameter so
240 that we could avoid the copy of the vector when returning.
242 dirlist.swap(argvec);
243 to avoid the copy. (Lgb)
245 /* A C++ implementaion will look like this:
246 string extension(ext);
247 if (extension[0] != '.') extension.insert(0, 1, '.');
248 vector<string> dirlist;
249 directory_iterator dit("dir");
250 while (dit != directory_iterator()) {
251 string fil = dit->filename;
252 if (prefixIs(fil, extension)) {
253 dirlist.push_back(fil);
257 dirlist.swap(argvec);
263 // Returns the real name of file name in directory path, with optional
265 string const FileSearch(string const & path, string const & name,
268 // if `name' is an absolute path, we ignore the setting of `path'
269 // Expand Environmentvariables in 'name'
270 string const tmpname = ReplaceEnvironmentPath(name);
271 string fullname = MakeAbsPath(tmpname, path);
272 // search first without extension, then with it.
273 if (IsFileReadable(fullname))
275 else if (ext.empty())
277 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
280 if (IsFileReadable(fullname))
288 // Search the file name.ext in the subdirectory dir of
290 // 2) build_lyxdir (if not empty)
292 string const LibFileSearch(string const & dir, string const & name,
295 string fullname = FileSearch(AddPath(user_lyxdir, dir), name, ext);
296 if (!fullname.empty())
299 if (!build_lyxdir().empty())
300 fullname = FileSearch(AddPath(build_lyxdir(), dir), name, ext);
301 if (!fullname.empty())
304 return FileSearch(AddPath(system_lyxdir(), dir), name, ext);
309 i18nLibFileSearch(string const & dir, string const & name,
312 // this comment is from intl/dcigettext.c. We try to mimick this
314 /* The highest priority value is the `LANGUAGE' environment
315 variable. But we don't use the value if the currently
316 selected locale is the C locale. This is a GNU extension. */
318 string const lc_all = GetEnv("LC_ALL");
319 string lang = GetEnv("LANGUAGE");
320 if (lang.empty() || lc_all == "C") {
323 lang = GetEnv("LANG");
327 lang = token(lang, '_', 0);
329 if (lang.empty() || lang == "C")
330 return LibFileSearch(dir, name, ext);
332 string const tmp = LibFileSearch(dir, lang + '_' + name,
337 return LibFileSearch(dir, name, ext);
342 string const LibScriptSearch(string const & command_in)
344 string const token_scriptpath("$$s/");
346 string command = command_in;
347 // Find the starting position of "$$s/"
348 string::size_type const pos1 = command.find(token_scriptpath);
349 if (pos1 == string::npos)
351 // Find the end of the "$$s/some_script" word within command
352 string::size_type const start_script = pos1 + 4;
353 string::size_type const pos2 = command.find(' ', start_script);
354 string::size_type const size_script = pos2 == string::npos?
355 (command.size() - start_script) : pos2 - start_script;
357 // Does this script file exist?
358 string const script =
359 LibFileSearch("scripts", command.substr(start_script, size_script));
361 if (script.empty()) {
362 // Replace "$$s/" with ""
363 command.erase(pos1, 4);
365 // Replace "$$s/some_script" with "$LYX_SCRIPT_PATH/some_script"
366 string::size_type const size_replace = size_script + 4;
367 command.replace(pos1, size_replace, script);
374 string const GetEnv(string const & envname)
376 // f.ex. what about error checking?
377 char const * const ch = getenv(envname.c_str());
378 string const envstr = !ch ? "" : ch;
383 string const GetEnvPath(string const & name)
386 string const pathlist = subst(GetEnv(name), ':', ';');
388 string const pathlist = os::slashify_path(GetEnv(name));
390 return rtrim(pathlist, ";");
396 int DeleteAllFilesInDir(string const & path)
398 // I have decided that we will be using parts from the boost
399 // library. Check out http://www.boost.org/
400 // For directory access we will then use the directory_iterator.
401 // Then the code will be something like:
402 // directory_iterator dit(path);
403 // directory_iterator dend;
404 // if (dit == dend) {
407 // for (; dit != dend; ++dit) {
408 // string filename(*dit);
409 // if (filename == "." || filename == "..")
411 // string unlinkpath(AddName(path, filename));
412 // lyx::unlink(unlinkpath);
415 DIR * dir = ::opendir(path.c_str());
420 int return_value = 0;
421 while ((de = readdir(dir))) {
422 string const temp = de->d_name;
423 if (temp == "." || temp == "..")
425 string const unlinkpath = AddName (path, temp);
427 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
431 FileInfo fi(unlinkpath);
432 if (fi.isOK() && fi.isDir())
433 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
434 deleted &= (unlink(unlinkpath) == 0);
443 string const CreateTmpDir(string const & tempdir, string const & mask)
446 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
447 << "CreateTmpDir: mask=`" << mask << '\'' << endl;
449 string const tmpfl(tempName(tempdir, mask));
450 // lyx::tempName actually creates a file to make sure that it
451 // stays unique. So we have to delete it before we can create
452 // a dir with the same name. Note also that we are not thread
453 // safe because of the gap between unlink and mkdir. (Lgb)
456 if (tmpfl.empty() || mkdir(tmpfl, 0700))
459 return MakeAbsPath(tmpfl);
465 int destroyDir(string const & tmpdir)
470 if (DeleteAllFilesInDir(tmpdir))
480 string const CreateBufferTmpDir(string const & pathfor)
483 static string const tmpdir(pathfor.empty() ? os::getTmpDir() : pathfor);
484 // We are in our own directory. Why bother to mangle name?
485 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
487 string const tmpfl = tmpdir + "/lyx_tmpbuf" + tostr(count++);
488 if (mkdir(tmpfl, 0777)) {
495 string const CreateLyXTmpDir(string const & deflt)
497 if ((!deflt.empty()) && (deflt != "/tmp")) {
498 if (mkdir(deflt, 0777)) {
502 return CreateTmpDir(deflt, "lyx_tmpdir");
509 return CreateTmpDir("/tmp", "lyx_tmpdir");
514 bool createDirectory(string const & path, int permission)
516 string temp(rtrim(os::slashify_path(path), "/"));
518 Assert(!temp.empty());
520 if (mkdir(temp, permission))
527 // Strip filename from path name
528 string const OnlyPath(string const & Filename)
530 // If empty filename, return empty
531 if (Filename.empty()) return Filename;
533 // Find last / or start of filename
534 string::size_type j = Filename.rfind('/');
535 if (j == string::npos)
537 return Filename.substr(0, j + 1);
541 // Convert relative path into absolute path based on a basepath.
542 // If relpath is absolute, just use that.
543 // If basepath is empty, use CWD as base.
544 string const MakeAbsPath(string const & RelPath, string const & BasePath)
546 // checks for already absolute path
547 if (os::is_absolute_path(RelPath))
550 // Copies given paths
551 string TempRel(os::slashify_path(RelPath));
552 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
553 TempRel = subst(TempRel, "//", "/");
557 if (os::is_absolute_path(BasePath))
560 TempBase = AddPath(getcwd(), BasePath);
562 // Handle /./ at the end of the path
563 while (suffixIs(TempBase, "/./"))
564 TempBase.erase(TempBase.length() - 2);
566 // processes relative path
567 string RTemp(TempRel);
570 while (!RTemp.empty()) {
572 RTemp = split(RTemp, Temp, '/');
574 if (Temp == ".") continue;
576 // Remove one level of TempBase
577 string::difference_type i = TempBase.length() - 2;
580 while (i > 0 && TempBase[i] != '/') --i;
584 while (i > 2 && TempBase[i] != '/') --i;
587 TempBase.erase(i, string::npos);
590 } else if (Temp.empty() && !RTemp.empty()) {
591 TempBase = os::current_root() + RTemp;
594 // Add this piece to TempBase
595 if (!suffixIs(TempBase, '/'))
601 // returns absolute path
602 return os::slashify_path(TempBase);
606 // Correctly append filename to the pathname.
607 // If pathname is '.', then don't use pathname.
608 // Chops any path of filename.
609 string const AddName(string const & path, string const & fname)
612 string const basename(OnlyFilename(fname));
616 if (path != "." && path != "./" && !path.empty()) {
617 buf = os::slashify_path(path);
618 if (!suffixIs(path, '/'))
622 return buf + basename;
626 // Strips path from filename
627 string const OnlyFilename(string const & fname)
632 string::size_type j = fname.rfind('/');
633 if (j == string::npos) // no '/' in fname
637 return fname.substr(j + 1);
641 /// Returns true is path is absolute
642 bool AbsolutePath(string const & path)
644 return os::is_absolute_path(path);
649 // Create absolute path. If impossible, don't do anything
650 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
651 string const ExpandPath(string const & path)
653 // checks for already absolute path
654 string RTemp(ReplaceEnvironmentPath(path));
655 if (os::is_absolute_path(RTemp))
659 string const copy(RTemp);
662 RTemp = split(RTemp, Temp, '/');
665 return getcwd() + '/' + RTemp;
668 return GetEnvPath("HOME") + '/' + RTemp;
671 return MakeAbsPath(copy);
673 // Don't know how to handle this
679 // Constracts path/../path
680 // Can't handle "../../" or "/../" (Asger)
681 // Also converts paths like /foo//bar ==> /foo/bar
682 string const NormalizePath(string const & path)
688 if (os::is_absolute_path(path))
691 // Make implicit current directory explicit
694 // Normalise paths like /foo//bar ==> /foo/bar
695 boost::RegEx regex("/{2,}");
696 RTemp = STRCONV(regex.Merge(STRCONV(RTemp), "/"));
698 while (!RTemp.empty()) {
700 RTemp = split(RTemp, Temp, '/');
704 } else if (Temp == "..") {
705 // Remove one level of TempBase
706 string::difference_type i = TempBase.length() - 2;
707 while (i > 0 && TempBase[i] != '/')
709 if (i >= 0 && TempBase[i] == '/')
710 TempBase.erase(i + 1, string::npos);
714 TempBase += Temp + '/';
718 // returns absolute path
723 string const GetFileContents(string const & fname)
725 FileInfo finfo(fname);
727 ifstream ifs(fname.c_str());
732 return STRCONV(ofs.str());
735 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
741 // Search ${...} as Variable-Name inside the string and replace it with
742 // the denoted environmentvariable
743 // Allow Variables according to
744 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
747 string const ReplaceEnvironmentPath(string const & path)
750 // CompareChar: Environment variables starts with this character
751 // PathChar: Next path component start with this character
752 // while CompareChar found do:
753 // Split String with PathChar
754 // Search Environmentvariable
755 // if found: Replace Strings
757 char const CompareChar = '$';
758 char const FirstChar = '{';
759 char const EndChar = '}';
760 char const UnderscoreChar = '_';
761 string EndString; EndString += EndChar;
762 string FirstString; FirstString += FirstChar;
763 string CompareString; CompareString += CompareChar;
764 string const RegExp("*}*"); // Exist EndChar inside a String?
766 // first: Search for a '$' - Sign.
768 string result1; //(copy); // for split-calls
769 string result0 = split(path, result1, CompareChar);
770 while (!result0.empty()) {
771 string copy1(result0); // contains String after $
773 // Check, if there is an EndChar inside original String.
775 if (!regexMatch(copy1, RegExp)) {
776 // No EndChar inside. So we are finished
777 result1 += CompareString + result0;
783 string res0 = split(copy1, res1, EndChar);
784 // Now res1 holds the environmentvariable
785 // First, check, if Contents is ok.
786 if (res1.empty()) { // No environmentvariable. Continue Loop.
787 result1 += CompareString + FirstString;
791 // check contents of res1
792 char const * res1_contents = res1.c_str();
793 if (*res1_contents != FirstChar) {
794 // Again No Environmentvariable
795 result1 += CompareString;
799 // Check for variable names
800 // Situation ${} is detected as "No Environmentvariable"
801 char const * cp1 = res1_contents + 1;
802 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
804 while (*cp1 && result) {
805 result = isalnum(*cp1) ||
806 (*cp1 == UnderscoreChar);
811 // no correct variable name
812 result1 += CompareString + res1 + EndString;
813 result0 = split(res0, res1, CompareChar);
818 string env(GetEnv(res1_contents + 1));
820 // Congratulations. Environmentvariable found
823 result1 += CompareString + res1 + EndString;
826 result0 = split(res0, res1, CompareChar);
833 // Make relative path out of two absolute paths
834 string const MakeRelPath(string const & abspath, string const & basepath)
835 // Makes relative path out of absolute path. If it is deeper than basepath,
836 // it's easy. If basepath and abspath share something (they are all deeper
837 // than some directory), it'll be rendered using ..'s. If they are completely
838 // different, then the absolute path will be used as relative path.
840 string::size_type const abslen = abspath.length();
841 string::size_type const baselen = basepath.length();
843 string::size_type i = os::common_path(abspath, basepath);
846 // actually no match - cannot make it relative
850 // Count how many dirs there are in basepath above match
851 // and append as many '..''s into relpath
853 string::size_type j = i;
854 while (j < baselen) {
855 if (basepath[j] == '/') {
856 if (j + 1 == baselen)
863 // Append relative stuff from common directory to abspath
864 if (abspath[i] == '/')
866 for (; i < abslen; ++i)
869 if (suffixIs(buf, '/'))
870 buf.erase(buf.length() - 1);
871 // Substitute empty with .
878 // Append sub-directory(ies) to a path in an intelligent way
879 string const AddPath(string const & path, string const & path_2)
882 string const path2 = os::slashify_path(path_2);
884 if (!path.empty() && path != "." && path != "./") {
885 buf = os::slashify_path(path);
886 if (path[path.length() - 1] != '/')
890 if (!path2.empty()) {
891 string::size_type const p2start = path2.find_first_not_of('/');
892 string::size_type const p2end = path2.find_last_not_of('/');
893 string const tmp = path2.substr(p2start, p2end - p2start + 1);
901 Change extension of oldname to extension.
902 Strips path off if no_path == true.
903 If no extension on oldname, just appends.
905 string const ChangeExtension(string const & oldname, string const & extension)
907 string::size_type const last_slash = oldname.rfind('/');
908 string::size_type last_dot = oldname.rfind('.');
909 if (last_dot < last_slash && last_slash != string::npos)
910 last_dot = string::npos;
913 // Make sure the extension starts with a dot
914 if (!extension.empty() && extension[0] != '.')
915 ext= '.' + extension;
919 return os::slashify_path(oldname.substr(0, last_dot) + ext);
923 /// Return the extension of the file (not including the .)
924 string const GetExtension(string const & name)
926 string::size_type const last_slash = name.rfind('/');
927 string::size_type const last_dot = name.rfind('.');
928 if (last_dot != string::npos &&
929 (last_slash == string::npos || last_dot > last_slash))
930 return name.substr(last_dot + 1,
931 name.length() - (last_dot + 1));
936 // the different filetypes and what they contain in one of the first lines
937 // (dots are any characters). (Herbert 20020131)
940 // EPS %!PS-Adobe-3.0 EPSF...
947 // PBM P1... or P4 (B/W)
948 // PGM P2... or P5 (Grayscale)
949 // PPM P3... or P6 (color)
950 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
951 // SGI \001\332... (decimal 474)
953 // TIFF II... or MM...
955 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
956 // ...static char *...
957 // XWD \000\000\000\151 (0x00006900) decimal 105
959 // GZIP \037\213\010\010... http://www.ietf.org/rfc/rfc1952.txt
960 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
961 // Z \037\177 UNIX compress
963 /// return the "extension" which belongs to the contents.
964 /// for no knowing contents return the extension. Without
965 /// an extension and unknown contents we return "user"
966 string const getExtFromContents(string const & filename)
969 if (filename.empty() || !IsFileReadable(filename))
973 ifstream ifs(filename.c_str());
975 // Couldn't open file...
979 string const gzipStamp = "\037\213\010\010";
982 string const zipStamp = "PK";
985 string const compressStamp = "\037\177";
987 // Maximum strings to read
988 int const max_count = 50;
992 bool firstLine = true;
993 while ((count++ < max_count) && format.empty()) {
995 lyxerr[Debug::GRAPHICS]
996 << "filetools(getExtFromContents)\n"
997 << "\tFile type not recognised before EOF!"
1003 string const stamp = str.substr(0,2);
1004 if (firstLine && str.size() >= 2) {
1005 // at first we check for a zipped file, because this
1006 // information is saved in the first bytes of the file!
1007 // also some graphic formats which save the information
1008 // in the first line, too.
1009 if (prefixIs(str, gzipStamp)) {
1012 } else if (stamp == zipStamp) {
1015 } else if (stamp == compressStamp) {
1016 format = "compress";
1018 // the graphics part
1019 } else if (stamp == "BM") {
1022 } else if (stamp == "\001\332") {
1026 // Don't need to use str.at(0), str.at(1) because
1027 // we already know that str.size() >= 2
1028 } else if (str[0] == 'P') {
1044 } else if ((stamp == "II") || (stamp == "MM")) {
1047 } else if (prefixIs(str,"%TGIF")) {
1050 } else if (prefixIs(str,"#FIG")) {
1053 } else if (prefixIs(str,"GIF")) {
1056 } else if (str.size() > 3) {
1057 int const c = ((str[0] << 24) & (str[1] << 16) &
1058 (str[2] << 8) & str[3]);
1067 if (!format.empty())
1069 else if (contains(str,"EPSF"))
1070 // dummy, if we have wrong file description like
1071 // %!PS-Adobe-2.0EPSF"
1074 else if (contains(str,"Grace"))
1077 else if (contains(str,"JFIF"))
1080 else if (contains(str,"%PDF"))
1083 else if (contains(str,"PNG"))
1086 else if (contains(str,"%!PS-Adobe")) {
1089 if (contains(str,"EPSF"))
1095 else if (contains(str,"_bits[]"))
1098 else if (contains(str,"XPM") || contains(str, "static char *"))
1101 else if (contains(str,"BITPIX"))
1105 if (!format.empty()) {
1106 lyxerr[Debug::GRAPHICS]
1107 << "Recognised Fileformat: " << format << endl;
1111 string const ext(GetExtension(filename));
1112 lyxerr[Debug::GRAPHICS]
1113 << "filetools(getExtFromContents)\n"
1114 << "\tCouldn't find a known Type!\n";
1116 lyxerr[Debug::GRAPHICS]
1117 << "\twill take the file extension -> "
1121 lyxerr[Debug::GRAPHICS]
1122 << "\twill use ext or a \"user\" defined format" << endl;
1128 /// check for zipped file
1129 bool zippedFile(string const & name)
1131 string const type = getExtFromContents(name);
1132 if (contains("gzip zip compress", type) && !type.empty())
1138 string const unzipFile(string const & zipped_file)
1140 string const file = ChangeExtension(zipped_file, string());
1141 string const tempfile = tempName(string(), file);
1143 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1145 one.startscript(Systemcall::Wait, command);
1146 // test that command was executed successfully (anon)
1147 // yes, please do. (Lgb)
1152 string const MakeDisplayPath(string const & path, unsigned int threshold)
1156 string const home(GetEnvPath("HOME"));
1158 // replace /home/blah with ~/
1159 if (prefixIs(str, home))
1160 str = subst(str, home, "~");
1162 if (str.length() <= threshold)
1165 string const prefix = ".../";
1168 while (str.length() > threshold)
1169 str = split(str, temp, '/');
1171 // Did we shorten everything away?
1173 // Yes, filename itself is too long.
1174 // Pick the start and the end of the filename.
1175 str = OnlyFilename(path);
1176 string const head = str.substr(0, threshold / 2 - 3);
1178 string::size_type len = str.length();
1180 str.substr(len - threshold / 2 - 2, len - 1);
1181 str = head + "..." + tail;
1184 return prefix + str;
1188 bool LyXReadLink(string const & file, string & link, bool resolve)
1190 char linkbuffer[512];
1191 // Should be PATH_MAX but that needs autconf support
1192 int const nRead = ::readlink(file.c_str(),
1193 linkbuffer, sizeof(linkbuffer) - 1);
1196 linkbuffer[nRead] = '\0'; // terminator
1198 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1205 cmd_ret const RunCommand(string const & cmd)
1207 // One question is if we should use popen or
1208 // create our own popen based on fork, exec, pipe
1209 // of course the best would be to have a
1210 // pstream (process stream), with the
1211 // variants ipstream, opstream
1213 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1215 // (Claus Hentschel) Check if popen was succesful ;-)
1217 return make_pair(-1, string());
1222 ret += static_cast<char>(c);
1225 int const pret = pclose(inf);
1226 return make_pair(pret, ret);
1230 string const findtexfile(string const & fil, string const & /*format*/)
1232 /* There is no problem to extend this function too use other
1233 methods to look for files. It could be setup to look
1234 in environment paths and also if wanted as a last resort
1235 to a recursive find. One of the easier extensions would
1236 perhaps be to use the LyX file lookup methods. But! I am
1237 going to implement this until I see some demand for it.
1241 // If the file can be found directly, we just return a
1242 // absolute path version of it.
1243 if (FileInfo(fil).exist())
1244 return MakeAbsPath(fil);
1246 // No we try to find it using kpsewhich.
1247 // It seems from the kpsewhich manual page that it is safe to use
1248 // kpsewhich without --format: "When the --format option is not
1249 // given, the search path used when looking for a file is inferred
1250 // from the name given, by looking for a known extension. If no
1251 // known extension is found, the search path for TeX source files
1253 // However, we want to take advantage of the format sine almost all
1254 // the different formats has environment variables that can be used
1255 // to controll which paths to search. f.ex. bib looks in
1256 // BIBINPUTS and TEXBIB. Small list follows:
1257 // bib - BIBINPUTS, TEXBIB
1259 // graphic/figure - TEXPICTS, TEXINPUTS
1260 // ist - TEXINDEXSTYLE, INDEXSTYLE
1261 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1263 // tfm - TFMFONTS, TEXFONTS
1264 // This means that to use kpsewhich in the best possible way we
1265 // should help it by setting additional path in the approp. envir.var.
1266 string const kpsecmd = "kpsewhich " + fil;
1268 cmd_ret const c = RunCommand(kpsecmd);
1270 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1271 << "kpse result = `" << rtrim(c.second, "\n")
1274 return os::internal_path(rtrim(c.second, "\n\r"));
1280 void removeAutosaveFile(string const & filename)
1282 string a = OnlyPath(filename);
1284 a += OnlyFilename(filename);
1286 FileInfo const fileinfo(a);
1287 if (fileinfo.exist())
1292 void readBB_lyxerrMessage(string const & file, bool & zipped,
1293 string const & message)
1295 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1296 << message << std::endl;
1297 #warning Why is this func deleting a file? (Lgb)
1303 string const readBB_from_PSFile(string const & file)
1305 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1306 // It seems that every command in the header has an own line,
1307 // getline() should work for all files.
1308 // On the other hand some plot programs write the bb at the
1309 // end of the file. Than we have in the header:
1310 // %%BoundingBox: (atend)
1311 // In this case we must check the end.
1312 bool zipped = zippedFile(file);
1313 string const file_ = zipped ?
1314 string(unzipFile(file)) : string(file);
1315 string const format = getExtFromContents(file_);
1317 if (format != "eps" && format != "ps") {
1318 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1322 std::ifstream is(file_.c_str());
1326 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1327 string const bb = ltrim(s.substr(14));
1328 readBB_lyxerrMessage(file_, zipped, bb);
1332 readBB_lyxerrMessage(file_, zipped, "no bb found");
1337 string const copyFileToDir(string const & path, string const & file_in)
1339 Assert(AbsolutePath(path));
1341 // First, make the file path relative to path.
1342 string file_out = MakeRelPath(path, NormalizePath(file_in));
1343 file_out = os::slashify_path(file_out);
1345 // Now generate a unique filename.
1346 // Remove the extension.
1347 file_out = ChangeExtension(file_out, string());
1348 // Replace '/' in the file name with '_'
1349 file_out = subst(file_out, "/", "_");
1350 // Replace '.' in the file name with '_'
1351 file_out = subst(file_out, ".", "_");
1352 // Append a unique ID
1354 file_out += '_' + tostr(id++);
1355 // Add the extension back on
1356 file_out = ChangeExtension(file_out, GetExtension(file_in));
1357 // Put this file in the buffer's temp dir
1358 file_out = MakeAbsPath(file_out, path);
1360 // If the original is newer than the copy, then copy the original
1361 // to the new directory.
1362 FileInfo fi(file_in);
1363 FileInfo fi2(file_out);
1365 bool success = true;
1368 difftime(fi.getModificationTime(),
1369 fi2.getModificationTime()) >= 0)
1370 success = copy(file_in, file_out);
1373 return success ? file_out : string();
1376 } //namespace support