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/tostr.h"
23 #include "support/systemcall.h"
24 #include "support/LAssert.h"
26 #include "filetools.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_in)
338 string const token_scriptpath("$$s/");
340 string command = command_in;
341 // Find the starting position of "$$s/"
342 string::size_type const pos1 = command.find(token_scriptpath);
343 if (pos1 == string::npos)
345 // Find the end of the "$$s/some_script" word within command
346 string::size_type const start_script = pos1 + 4;
347 string::size_type const pos2 = command.find(' ', start_script);
348 string::size_type const size_script = pos2 == string::npos?
349 (command.size() - start_script) : pos2 - start_script;
351 // Does this script file exist?
352 string const script =
353 LibFileSearch("scripts", command.substr(start_script, size_script));
355 if (script.empty()) {
356 // Replace "$$s/" with ""
357 command.erase(pos1, 4);
359 // Replace "$$s/some_script" with "$LYX_SCRIPT_PATH/some_script"
360 string::size_type const size_replace = size_script + 4;
361 command.replace(pos1, size_replace, script);
368 string const GetEnv(string const & envname)
370 // f.ex. what about error checking?
371 char const * const ch = getenv(envname.c_str());
372 string const envstr = !ch ? "" : ch;
377 string const GetEnvPath(string const & name)
380 string const pathlist = subst(GetEnv(name), ':', ';');
382 string const pathlist = os::slashify_path(GetEnv(name));
384 return rtrim(pathlist, ";");
390 int DeleteAllFilesInDir(string const & path)
392 // I have decided that we will be using parts from the boost
393 // library. Check out http://www.boost.org/
394 // For directory access we will then use the directory_iterator.
395 // Then the code will be something like:
396 // directory_iterator dit(path);
397 // directory_iterator dend;
398 // if (dit == dend) {
401 // for (; dit != dend; ++dit) {
402 // string filename(*dit);
403 // if (filename == "." || filename == "..")
405 // string unlinkpath(AddName(path, filename));
406 // lyx::unlink(unlinkpath);
409 DIR * dir = ::opendir(path.c_str());
414 int return_value = 0;
415 while ((de = readdir(dir))) {
416 string const temp = de->d_name;
417 if (temp == "." || temp == "..")
419 string const unlinkpath = AddName (path, temp);
421 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
425 FileInfo fi(unlinkpath);
426 if (fi.isOK() && fi.isDir())
427 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
428 deleted &= (lyx::unlink(unlinkpath) == 0);
437 string const CreateTmpDir(string const & tempdir, string const & mask)
440 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
441 << "CreateTmpDir: mask=`" << mask << '\'' << endl;
443 string const tmpfl(lyx::tempName(tempdir, mask));
444 // lyx::tempName actually creates a file to make sure that it
445 // stays unique. So we have to delete it before we can create
446 // a dir with the same name. Note also that we are not thread
447 // safe because of the gap between unlink and mkdir. (Lgb)
448 lyx::unlink(tmpfl.c_str());
450 if (tmpfl.empty() || lyx::mkdir(tmpfl, 0700))
453 return MakeAbsPath(tmpfl);
459 int destroyDir(string const & tmpdir)
464 if (DeleteAllFilesInDir(tmpdir))
467 if (lyx::rmdir(tmpdir))
474 string const CreateBufferTmpDir(string const & pathfor)
477 static string const tmpdir(pathfor.empty() ? os::getTmpDir() : pathfor);
478 // We are in our own directory. Why bother to mangle name?
479 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
481 string const tmpfl = tmpdir + "/lyx_tmpbuf" + tostr(count++);
482 if (lyx::mkdir(tmpfl, 0777)) {
489 string const CreateLyXTmpDir(string const & deflt)
491 if ((!deflt.empty()) && (deflt != "/tmp")) {
492 if (lyx::mkdir(deflt, 0777)) {
496 return CreateTmpDir(deflt, "lyx_tmpdir");
503 return CreateTmpDir("/tmp", "lyx_tmpdir");
508 bool createDirectory(string const & path, int permission)
510 string temp(rtrim(os::slashify_path(path), "/"));
512 lyx::Assert(!temp.empty());
514 if (lyx::mkdir(temp, permission))
521 // Strip filename from path name
522 string const OnlyPath(string const & Filename)
524 // If empty filename, return empty
525 if (Filename.empty()) return Filename;
527 // Find last / or start of filename
528 string::size_type j = Filename.rfind('/');
529 if (j == string::npos)
531 return Filename.substr(0, j + 1);
535 // Convert relative path into absolute path based on a basepath.
536 // If relpath is absolute, just use that.
537 // If basepath is empty, use CWD as base.
538 string const MakeAbsPath(string const & RelPath, string const & BasePath)
540 // checks for already absolute path
541 if (os::is_absolute_path(RelPath))
544 // Copies given paths
545 string TempRel(os::slashify_path(RelPath));
546 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
547 TempRel = subst(TempRel, "//", "/");
551 if (os::is_absolute_path(BasePath))
554 TempBase = AddPath(lyx::getcwd(), BasePath);
556 // Handle /./ at the end of the path
557 while (suffixIs(TempBase, "/./"))
558 TempBase.erase(TempBase.length() - 2);
560 // processes relative path
561 string RTemp(TempRel);
564 while (!RTemp.empty()) {
566 RTemp = split(RTemp, Temp, '/');
568 if (Temp == ".") continue;
570 // Remove one level of TempBase
571 string::difference_type i = TempBase.length() - 2;
574 while (i > 0 && TempBase[i] != '/') --i;
578 while (i > 2 && TempBase[i] != '/') --i;
581 TempBase.erase(i, string::npos);
584 } else if (Temp.empty() && !RTemp.empty()) {
585 TempBase = os::current_root() + RTemp;
588 // Add this piece to TempBase
589 if (!suffixIs(TempBase, '/'))
595 // returns absolute path
596 return os::slashify_path(TempBase);
600 // Correctly append filename to the pathname.
601 // If pathname is '.', then don't use pathname.
602 // Chops any path of filename.
603 string const AddName(string const & path, string const & fname)
606 string const basename(OnlyFilename(fname));
610 if (path != "." && path != "./" && !path.empty()) {
611 buf = os::slashify_path(path);
612 if (!suffixIs(path, '/'))
616 return buf + basename;
620 // Strips path from filename
621 string const OnlyFilename(string const & fname)
626 string::size_type j = fname.rfind('/');
627 if (j == string::npos) // no '/' in fname
631 return fname.substr(j + 1);
635 /// Returns true is path is absolute
636 bool AbsolutePath(string const & path)
638 return os::is_absolute_path(path);
643 // Create absolute path. If impossible, don't do anything
644 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
645 string const ExpandPath(string const & path)
647 // checks for already absolute path
648 string RTemp(ReplaceEnvironmentPath(path));
649 if (os::is_absolute_path(RTemp))
653 string const copy(RTemp);
656 RTemp = split(RTemp, Temp, '/');
659 return lyx::getcwd() /*GetCWD()*/ + '/' + RTemp;
662 return GetEnvPath("HOME") + '/' + RTemp;
665 return MakeAbsPath(copy);
667 // Don't know how to handle this
673 // Constracts path/../path
674 // Can't handle "../../" or "/../" (Asger)
675 string const NormalizePath(string const & path)
681 if (os::is_absolute_path(path))
684 // Make implicit current directory explicit
687 while (!RTemp.empty()) {
689 RTemp = split(RTemp, Temp, '/');
693 } else if (Temp == "..") {
694 // Remove one level of TempBase
695 string::difference_type i = TempBase.length() - 2;
696 while (i > 0 && TempBase[i] != '/')
698 if (i >= 0 && TempBase[i] == '/')
699 TempBase.erase(i + 1, string::npos);
703 TempBase += Temp + '/';
707 // returns absolute path
712 string const GetFileContents(string const & fname)
714 FileInfo finfo(fname);
716 ifstream ifs(fname.c_str());
721 return STRCONV(ofs.str());
724 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
730 // Search ${...} as Variable-Name inside the string and replace it with
731 // the denoted environmentvariable
732 // Allow Variables according to
733 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
736 string const ReplaceEnvironmentPath(string const & path)
739 // CompareChar: Environment variables starts with this character
740 // PathChar: Next path component start with this character
741 // while CompareChar found do:
742 // Split String with PathChar
743 // Search Environmentvariable
744 // if found: Replace Strings
746 char const CompareChar = '$';
747 char const FirstChar = '{';
748 char const EndChar = '}';
749 char const UnderscoreChar = '_';
750 string EndString; EndString += EndChar;
751 string FirstString; FirstString += FirstChar;
752 string CompareString; CompareString += CompareChar;
753 string const RegExp("*}*"); // Exist EndChar inside a String?
755 // first: Search for a '$' - Sign.
757 string result1; //(copy); // for split-calls
758 string result0 = split(path, result1, CompareChar);
759 while (!result0.empty()) {
760 string copy1(result0); // contains String after $
762 // Check, if there is an EndChar inside original String.
764 if (!regexMatch(copy1, RegExp)) {
765 // No EndChar inside. So we are finished
766 result1 += CompareString + result0;
772 string res0 = split(copy1, res1, EndChar);
773 // Now res1 holds the environmentvariable
774 // First, check, if Contents is ok.
775 if (res1.empty()) { // No environmentvariable. Continue Loop.
776 result1 += CompareString + FirstString;
780 // check contents of res1
781 char const * res1_contents = res1.c_str();
782 if (*res1_contents != FirstChar) {
783 // Again No Environmentvariable
784 result1 += CompareString;
788 // Check for variable names
789 // Situation ${} is detected as "No Environmentvariable"
790 char const * cp1 = res1_contents + 1;
791 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
793 while (*cp1 && result) {
794 result = isalnum(*cp1) ||
795 (*cp1 == UnderscoreChar);
800 // no correct variable name
801 result1 += CompareString + res1 + EndString;
802 result0 = split(res0, res1, CompareChar);
807 string env(GetEnv(res1_contents + 1));
809 // Congratulations. Environmentvariable found
812 result1 += CompareString + res1 + EndString;
815 result0 = split(res0, res1, CompareChar);
822 // Make relative path out of two absolute paths
823 string const MakeRelPath(string const & abspath, string const & basepath)
824 // Makes relative path out of absolute path. If it is deeper than basepath,
825 // it's easy. If basepath and abspath share something (they are all deeper
826 // than some directory), it'll be rendered using ..'s. If they are completely
827 // different, then the absolute path will be used as relative path.
829 string::size_type const abslen = abspath.length();
830 string::size_type const baselen = basepath.length();
832 string::size_type i = os::common_path(abspath, basepath);
835 // actually no match - cannot make it relative
839 // Count how many dirs there are in basepath above match
840 // and append as many '..''s into relpath
842 string::size_type j = i;
843 while (j < baselen) {
844 if (basepath[j] == '/') {
845 if (j + 1 == baselen)
852 // Append relative stuff from common directory to abspath
853 if (abspath[i] == '/')
855 for (; i < abslen; ++i)
858 if (suffixIs(buf, '/'))
859 buf.erase(buf.length() - 1);
860 // Substitute empty with .
867 // Append sub-directory(ies) to a path in an intelligent way
868 string const AddPath(string const & path, string const & path_2)
871 string const path2 = os::slashify_path(path_2);
873 if (!path.empty() && path != "." && path != "./") {
874 buf = os::slashify_path(path);
875 if (path[path.length() - 1] != '/')
879 if (!path2.empty()) {
880 string::size_type const p2start = path2.find_first_not_of('/');
881 string::size_type const p2end = path2.find_last_not_of('/');
882 string const tmp = path2.substr(p2start, p2end - p2start + 1);
890 Change extension of oldname to extension.
891 Strips path off if no_path == true.
892 If no extension on oldname, just appends.
894 string const ChangeExtension(string const & oldname, string const & extension)
896 string::size_type const last_slash = oldname.rfind('/');
897 string::size_type last_dot = oldname.rfind('.');
898 if (last_dot < last_slash && last_slash != string::npos)
899 last_dot = string::npos;
902 // Make sure the extension starts with a dot
903 if (!extension.empty() && extension[0] != '.')
904 ext= '.' + extension;
908 return os::slashify_path(oldname.substr(0, last_dot) + ext);
912 /// Return the extension of the file (not including the .)
913 string const GetExtension(string const & name)
915 string::size_type const last_slash = name.rfind('/');
916 string::size_type const last_dot = name.rfind('.');
917 if (last_dot != string::npos &&
918 (last_slash == string::npos || last_dot > last_slash))
919 return name.substr(last_dot + 1,
920 name.length() - (last_dot + 1));
925 // the different filetypes and what they contain in one of the first lines
926 // (dots are any characters). (Herbert 20020131)
929 // EPS %!PS-Adobe-3.0 EPSF...
936 // PBM P1... or P4 (B/W)
937 // PGM P2... or P5 (Grayscale)
938 // PPM P3... or P6 (color)
939 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
940 // SGI \001\332... (decimal 474)
942 // TIFF II... or MM...
944 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
945 // ...static char *...
946 // XWD \000\000\000\151 (0x00006900) decimal 105
948 // GZIP \037\213\010\010... http://www.ietf.org/rfc/rfc1952.txt
949 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
950 // Z \037\177 UNIX compress
952 /// return the "extension" which belongs to the contents.
953 /// for no knowing contents return the extension. Without
954 /// an extension and unknown contents we return "user"
955 string const getExtFromContents(string const & filename)
958 if (filename.empty() || !IsFileReadable(filename))
962 ifstream ifs(filename.c_str());
964 // Couldn't open file...
968 string const gzipStamp = "\037\213\010\010";
971 string const zipStamp = "PK";
974 string const compressStamp = "\037\177";
976 // Maximum strings to read
977 int const max_count = 50;
981 bool firstLine = true;
982 while ((count++ < max_count) && format.empty()) {
984 lyxerr[Debug::GRAPHICS]
985 << "filetools(getExtFromContents)\n"
986 << "\tFile type not recognised before EOF!"
992 string const stamp = str.substr(0,2);
993 if (firstLine && str.size() >= 2) {
994 // at first we check for a zipped file, because this
995 // information is saved in the first bytes of the file!
996 // also some graphic formats which save the information
997 // in the first line, too.
998 if (prefixIs(str, gzipStamp)) {
1001 } else if (stamp == zipStamp) {
1004 } else if (stamp == compressStamp) {
1005 format = "compress";
1007 // the graphics part
1008 } else if (stamp == "BM") {
1011 } else if (stamp == "\001\332") {
1015 // Don't need to use str.at(0), str.at(1) because
1016 // we already know that str.size() >= 2
1017 } else if (str[0] == 'P') {
1033 } else if ((stamp == "II") || (stamp == "MM")) {
1036 } else if (prefixIs(str,"%TGIF")) {
1039 } else if (prefixIs(str,"#FIG")) {
1042 } else if (prefixIs(str,"GIF")) {
1045 } else if (str.size() > 3) {
1046 int const c = ((str[0] << 24) & (str[1] << 16) &
1047 (str[2] << 8) & str[3]);
1056 if (!format.empty())
1058 else if (contains(str,"EPSF"))
1059 // dummy, if we have wrong file description like
1060 // %!PS-Adobe-2.0EPSF"
1063 else if (contains(str,"Grace"))
1066 else if (contains(str,"JFIF"))
1069 else if (contains(str,"%PDF"))
1072 else if (contains(str,"PNG"))
1075 else if (contains(str,"%!PS-Adobe")) {
1078 if (contains(str,"EPSF"))
1084 else if (contains(str,"_bits[]"))
1087 else if (contains(str,"XPM") || contains(str, "static char *"))
1090 else if (contains(str,"BITPIX"))
1094 if (!format.empty()) {
1095 lyxerr[Debug::GRAPHICS]
1096 << "Recognised Fileformat: " << format << endl;
1100 string const ext(GetExtension(filename));
1101 lyxerr[Debug::GRAPHICS]
1102 << "filetools(getExtFromContents)\n"
1103 << "\tCouldn't find a known Type!\n";
1105 lyxerr[Debug::GRAPHICS]
1106 << "\twill take the file extension -> "
1110 lyxerr[Debug::GRAPHICS]
1111 << "\twill use ext or a \"user\" defined format" << endl;
1117 /// check for zipped file
1118 bool zippedFile(string const & name)
1120 string const type = getExtFromContents(name);
1121 if (contains("gzip zip compress", type) && !type.empty())
1127 string const unzipFile(string const & zipped_file)
1129 string const file = ChangeExtension(zipped_file, string());
1130 string const tempfile = lyx::tempName(string(), file);
1132 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1134 one.startscript(Systemcall::Wait, command);
1135 // test that command was executed successfully (anon)
1136 // yes, please do. (Lgb)
1141 string const MakeDisplayPath(string const & path, unsigned int threshold)
1145 string const home(GetEnvPath("HOME"));
1147 // replace /home/blah with ~/
1148 if (prefixIs(str, home))
1149 str = subst(str, home, "~");
1151 if (str.length() <= threshold)
1154 string const prefix = ".../";
1157 while (str.length() > threshold)
1158 str = split(str, temp, '/');
1160 // Did we shorten everything away?
1162 // Yes, filename itself is too long.
1163 // Pick the start and the end of the filename.
1164 str = OnlyFilename(path);
1165 string const head = str.substr(0, threshold / 2 - 3);
1167 string::size_type len = str.length();
1169 str.substr(len - threshold / 2 - 2, len - 1);
1170 str = head + "..." + tail;
1173 return prefix + str;
1177 bool LyXReadLink(string const & file, string & link, bool resolve)
1179 char linkbuffer[512];
1180 // Should be PATH_MAX but that needs autconf support
1181 int const nRead = ::readlink(file.c_str(),
1182 linkbuffer, sizeof(linkbuffer) - 1);
1185 linkbuffer[nRead] = '\0'; // terminator
1187 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1194 cmd_ret const RunCommand(string const & cmd)
1196 // One question is if we should use popen or
1197 // create our own popen based on fork, exec, pipe
1198 // of course the best would be to have a
1199 // pstream (process stream), with the
1200 // variants ipstream, opstream
1202 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1204 // (Claus Hentschel) Check if popen was succesful ;-)
1206 return make_pair(-1, string());
1211 ret += static_cast<char>(c);
1214 int const pret = pclose(inf);
1215 return make_pair(pret, ret);
1219 string const findtexfile(string const & fil, string const & /*format*/)
1221 /* There is no problem to extend this function too use other
1222 methods to look for files. It could be setup to look
1223 in environment paths and also if wanted as a last resort
1224 to a recursive find. One of the easier extensions would
1225 perhaps be to use the LyX file lookup methods. But! I am
1226 going to implement this until I see some demand for it.
1230 // If the file can be found directly, we just return a
1231 // absolute path version of it.
1232 if (FileInfo(fil).exist())
1233 return MakeAbsPath(fil);
1235 // No we try to find it using kpsewhich.
1236 // It seems from the kpsewhich manual page that it is safe to use
1237 // kpsewhich without --format: "When the --format option is not
1238 // given, the search path used when looking for a file is inferred
1239 // from the name given, by looking for a known extension. If no
1240 // known extension is found, the search path for TeX source files
1242 // However, we want to take advantage of the format sine almost all
1243 // the different formats has environment variables that can be used
1244 // to controll which paths to search. f.ex. bib looks in
1245 // BIBINPUTS and TEXBIB. Small list follows:
1246 // bib - BIBINPUTS, TEXBIB
1248 // graphic/figure - TEXPICTS, TEXINPUTS
1249 // ist - TEXINDEXSTYLE, INDEXSTYLE
1250 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1252 // tfm - TFMFONTS, TEXFONTS
1253 // This means that to use kpsewhich in the best possible way we
1254 // should help it by setting additional path in the approp. envir.var.
1255 string const kpsecmd = "kpsewhich " + fil;
1257 cmd_ret const c = RunCommand(kpsecmd);
1259 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1260 << "kpse result = `" << rtrim(c.second, "\n")
1263 return os::internal_path(rtrim(c.second, "\n\r"));
1269 void removeAutosaveFile(string const & filename)
1271 string a = OnlyPath(filename);
1273 a += OnlyFilename(filename);
1275 FileInfo const fileinfo(a);
1276 if (fileinfo.exist())
1281 void readBB_lyxerrMessage(string const & file, bool & zipped,
1282 string const & message)
1284 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1285 << message << std::endl;
1291 string const readBB_from_PSFile(string const & file)
1293 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1294 // It seems that every command in the header has an own line,
1295 // getline() should work for all files.
1296 // On the other hand some plot programs write the bb at the
1297 // end of the file. Than we have in the header:
1298 // %%BoundingBox: (atend)
1299 // In this case we must check the end.
1300 bool zipped = zippedFile(file);
1301 string const file_ = zipped ?
1302 string(unzipFile(file)) : string(file);
1303 string const format = getExtFromContents(file_);
1305 if (format != "eps" && format != "ps") {
1306 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1310 std::ifstream is(file_.c_str());
1314 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1315 string const bb = ltrim(s.substr(14));
1316 readBB_lyxerrMessage(file_, zipped, bb);
1320 readBB_lyxerrMessage(file_, zipped, "no bb found");
1325 string copyFileToDir(string const & path, string const & file_in)
1327 lyx::Assert(AbsolutePath(path));
1329 // First, make the file path relative to path.
1330 string file_out = MakeRelPath(path, NormalizePath(file_in));
1331 file_out = os::slashify_path(file_out);
1333 // Now generate a unique filename.
1334 // Remove the extension.
1335 file_out = ChangeExtension(file_out, string());
1336 // Replace '/' in the file name with '_'
1337 file_out = subst(file_out, "/", "_");
1338 // Replace '.' in the file name with '_'
1339 file_out = subst(file_out, ".", "_");
1340 // Append a unique ID
1342 file_out += '_' + tostr(id++);
1343 // Add the extension back on
1344 file_out = ChangeExtension(file_out, GetExtension(file_in));
1345 // Put this file in the buffer's temp dir
1346 file_out = MakeAbsPath(file_out, path);
1348 // If the original is newer than the copy, then copy the original
1349 // to the new directory.
1350 FileInfo fi(file_in);
1351 FileInfo fi2(file_out);
1353 bool success = true;
1356 difftime(fi.getModificationTime(),
1357 fi2.getModificationTime()) >= 0)
1358 success = lyx::copy(file_in, file_out);
1361 return success ? file_out : string();