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)
50 // Which part of this is still necessary? (JMarc).
53 # define NAMLEN(dirent) strlen((dirent)->d_name)
55 # define dirent direct
56 # define NAMLEN(dirent) (dirent)->d_namlen
58 # include <sys/ndir.h>
68 #ifndef CXX_GLOBAL_CSTD
81 extern string system_lyxdir;
82 extern string build_lyxdir;
83 extern string user_lyxdir;
86 bool IsLyXFilename(string const & filename)
88 return suffixIs(ascii_lowercase(filename), ".lyx");
92 bool IsSGMLFilename(string const & filename)
94 return suffixIs(ascii_lowercase(filename), ".sgml");
98 // Substitutes spaces with underscores in filename (and path)
99 string const MakeLatexName(string const & file)
101 string name = OnlyFilename(file);
102 string const path = OnlyPath(file);
104 for (string::size_type i = 0; i < name.length(); ++i) {
105 name[i] &= 0x7f; // set 8th bit to 0
108 // ok so we scan through the string twice, but who cares.
109 string const keep("abcdefghijklmnopqrstuvwxyz"
110 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
111 "@!\"'()*+,-./0123456789:;<=>?[]`|");
113 string::size_type pos = 0;
114 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
117 return AddName(path, name);
121 // Substitutes spaces with underscores in filename (and path)
122 string const QuoteName(string const & name)
124 return (os::shell() == os::UNIX) ?
130 // Is a file readable ?
131 bool IsFileReadable(string const & path)
134 return file.isOK() && file.isRegular() && file.readable();
138 // Is a file read_only?
139 // return 1 read-write
141 // -1 error (doesn't exist, no access, anything else)
142 int IsFileWriteable(string const & path)
146 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
148 if (fi.readable()) // read-only
150 return -1; // everything else.
154 //returns true: dir writeable
155 // false: not writeable
156 bool IsDirWriteable(string const & path)
158 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
160 string const tmpfl(lyx::tempName(path, "lyxwritetest"));
170 // Uses a string of paths separated by ";"s to find a file to open.
171 // Can't cope with pathnames with a ';' in them. Returns full path to file.
172 // If path entry begins with $$LyX/, use system_lyxdir
173 // If path entry begins with $$User/, use user_lyxdir
174 // Example: "$$User/doc;$$LyX/doc"
175 string const FileOpenSearch(string const & path, string const & name,
180 bool notfound = true;
181 string tmppath = split(path, path_element, ';');
183 while (notfound && !path_element.empty()) {
184 path_element = os::slashify_path(path_element);
185 if (!suffixIs(path_element, '/'))
187 path_element = subst(path_element, "$$LyX", system_lyxdir);
188 path_element = subst(path_element, "$$User", user_lyxdir);
190 real_file = FileSearch(path_element, name, ext);
192 if (real_file.empty()) {
194 tmppath = split(tmppath, path_element, ';');
195 } while (!tmppath.empty() && path_element.empty());
201 if (ext.empty() && notfound) {
202 real_file = FileOpenSearch(path, name, "exe");
203 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
210 /// Returns a vector of all files in directory dir having extension ext.
211 vector<string> const DirList(string const & dir, string const & ext)
213 // This is a non-error checking C/system implementation
215 if (!ext.empty() && ext[0] != '.')
219 vector<string> dirlist;
220 DIR * dirp = ::opendir(dir.c_str());
223 << "Directory \"" << dir
224 << "\" does not exist to DirList." << endl;
229 while ((dire = ::readdir(dirp))) {
230 string const fil = dire->d_name;
231 if (suffixIs(fil, extension)) {
232 dirlist.push_back(fil);
237 /* I would have prefered to take a vector<string>& as parameter so
238 that we could avoid the copy of the vector when returning.
240 dirlist.swap(argvec);
241 to avoid the copy. (Lgb)
243 /* A C++ implementaion will look like this:
244 string extension(ext);
245 if (extension[0] != '.') extension.insert(0, 1, '.');
246 vector<string> dirlist;
247 directory_iterator dit("dir");
248 while (dit != directory_iterator()) {
249 string fil = dit->filename;
250 if (prefixIs(fil, extension)) {
251 dirlist.push_back(fil);
255 dirlist.swap(argvec);
261 // Returns the real name of file name in directory path, with optional
263 string const FileSearch(string const & path, string const & name,
266 // if `name' is an absolute path, we ignore the setting of `path'
267 // Expand Environmentvariables in 'name'
268 string const tmpname = ReplaceEnvironmentPath(name);
269 string fullname = MakeAbsPath(tmpname, path);
270 // search first without extension, then with it.
271 if (IsFileReadable(fullname))
273 else if (ext.empty())
275 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
278 if (IsFileReadable(fullname))
286 // Search the file name.ext in the subdirectory dir of
288 // 2) build_lyxdir (if not empty)
290 string const LibFileSearch(string const & dir, string const & name,
293 string fullname = FileSearch(AddPath(user_lyxdir, dir), name, ext);
294 if (!fullname.empty())
297 if (!build_lyxdir.empty())
298 fullname = FileSearch(AddPath(build_lyxdir, dir), name, ext);
299 if (!fullname.empty())
302 return FileSearch(AddPath(system_lyxdir, dir), name, ext);
307 i18nLibFileSearch(string const & dir, string const & name,
310 // this comment is from intl/dcigettext.c. We try to mimick this
312 /* The highest priority value is the `LANGUAGE' environment
313 variable. But we don't use the value if the currently
314 selected locale is the C locale. This is a GNU extension. */
316 string const lc_all = GetEnv("LC_ALL");
317 string lang = GetEnv("LANGUAGE");
318 if (lang.empty() || lc_all == "C") {
321 lang = GetEnv("LANG");
325 lang = token(lang, '_', 0);
327 if (lang.empty() || lang == "C")
328 return LibFileSearch(dir, name, ext);
330 string const tmp = LibFileSearch(dir, lang + '_' + name,
335 return LibFileSearch(dir, name, ext);
340 string const LibScriptSearch(string const & command_in)
342 string const token_scriptpath("$$s/");
344 string command = command_in;
345 // Find the starting position of "$$s/"
346 string::size_type const pos1 = command.find(token_scriptpath);
347 if (pos1 == string::npos)
349 // Find the end of the "$$s/some_script" word within command
350 string::size_type const start_script = pos1 + 4;
351 string::size_type const pos2 = command.find(' ', start_script);
352 string::size_type const size_script = pos2 == string::npos?
353 (command.size() - start_script) : pos2 - start_script;
355 // Does this script file exist?
356 string const script =
357 LibFileSearch("scripts", command.substr(start_script, size_script));
359 if (script.empty()) {
360 // Replace "$$s/" with ""
361 command.erase(pos1, 4);
363 // Replace "$$s/some_script" with "$LYX_SCRIPT_PATH/some_script"
364 string::size_type const size_replace = size_script + 4;
365 command.replace(pos1, size_replace, script);
372 string const GetEnv(string const & envname)
374 // f.ex. what about error checking?
375 char const * const ch = getenv(envname.c_str());
376 string const envstr = !ch ? "" : ch;
381 string const GetEnvPath(string const & name)
384 string const pathlist = subst(GetEnv(name), ':', ';');
386 string const pathlist = os::slashify_path(GetEnv(name));
388 return rtrim(pathlist, ";");
394 int DeleteAllFilesInDir(string const & path)
396 // I have decided that we will be using parts from the boost
397 // library. Check out http://www.boost.org/
398 // For directory access we will then use the directory_iterator.
399 // Then the code will be something like:
400 // directory_iterator dit(path);
401 // directory_iterator dend;
402 // if (dit == dend) {
405 // for (; dit != dend; ++dit) {
406 // string filename(*dit);
407 // if (filename == "." || filename == "..")
409 // string unlinkpath(AddName(path, filename));
410 // lyx::unlink(unlinkpath);
413 DIR * dir = ::opendir(path.c_str());
418 int return_value = 0;
419 while ((de = readdir(dir))) {
420 string const temp = de->d_name;
421 if (temp == "." || temp == "..")
423 string const unlinkpath = AddName (path, temp);
425 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
429 FileInfo fi(unlinkpath);
430 if (fi.isOK() && fi.isDir())
431 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
432 deleted &= (lyx::unlink(unlinkpath) == 0);
441 string const CreateTmpDir(string const & tempdir, string const & mask)
444 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
445 << "CreateTmpDir: mask=`" << mask << '\'' << endl;
447 string const tmpfl(lyx::tempName(tempdir, mask));
448 // lyx::tempName actually creates a file to make sure that it
449 // stays unique. So we have to delete it before we can create
450 // a dir with the same name. Note also that we are not thread
451 // safe because of the gap between unlink and mkdir. (Lgb)
452 lyx::unlink(tmpfl.c_str());
454 if (tmpfl.empty() || lyx::mkdir(tmpfl, 0700))
457 return MakeAbsPath(tmpfl);
463 int destroyDir(string const & tmpdir)
468 if (DeleteAllFilesInDir(tmpdir))
471 if (lyx::rmdir(tmpdir))
478 string const CreateBufferTmpDir(string const & pathfor)
481 static string const tmpdir(pathfor.empty() ? os::getTmpDir() : pathfor);
482 // We are in our own directory. Why bother to mangle name?
483 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
485 string const tmpfl = tmpdir + "/lyx_tmpbuf" + tostr(count++);
486 if (lyx::mkdir(tmpfl, 0777)) {
493 string const CreateLyXTmpDir(string const & deflt)
495 if ((!deflt.empty()) && (deflt != "/tmp")) {
496 if (lyx::mkdir(deflt, 0777)) {
500 return CreateTmpDir(deflt, "lyx_tmpdir");
507 return CreateTmpDir("/tmp", "lyx_tmpdir");
512 bool createDirectory(string const & path, int permission)
514 string temp(rtrim(os::slashify_path(path), "/"));
516 lyx::Assert(!temp.empty());
518 if (lyx::mkdir(temp, permission))
525 // Strip filename from path name
526 string const OnlyPath(string const & Filename)
528 // If empty filename, return empty
529 if (Filename.empty()) return Filename;
531 // Find last / or start of filename
532 string::size_type j = Filename.rfind('/');
533 if (j == string::npos)
535 return Filename.substr(0, j + 1);
539 // Convert relative path into absolute path based on a basepath.
540 // If relpath is absolute, just use that.
541 // If basepath is empty, use CWD as base.
542 string const MakeAbsPath(string const & RelPath, string const & BasePath)
544 // checks for already absolute path
545 if (os::is_absolute_path(RelPath))
548 // Copies given paths
549 string TempRel(os::slashify_path(RelPath));
550 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
551 TempRel = subst(TempRel, "//", "/");
555 if (os::is_absolute_path(BasePath))
558 TempBase = AddPath(lyx::getcwd(), BasePath);
560 // Handle /./ at the end of the path
561 while (suffixIs(TempBase, "/./"))
562 TempBase.erase(TempBase.length() - 2);
564 // processes relative path
565 string RTemp(TempRel);
568 while (!RTemp.empty()) {
570 RTemp = split(RTemp, Temp, '/');
572 if (Temp == ".") continue;
574 // Remove one level of TempBase
575 string::difference_type i = TempBase.length() - 2;
578 while (i > 0 && TempBase[i] != '/') --i;
582 while (i > 2 && TempBase[i] != '/') --i;
585 TempBase.erase(i, string::npos);
588 } else if (Temp.empty() && !RTemp.empty()) {
589 TempBase = os::current_root() + RTemp;
592 // Add this piece to TempBase
593 if (!suffixIs(TempBase, '/'))
599 // returns absolute path
600 return os::slashify_path(TempBase);
604 // Correctly append filename to the pathname.
605 // If pathname is '.', then don't use pathname.
606 // Chops any path of filename.
607 string const AddName(string const & path, string const & fname)
610 string const basename(OnlyFilename(fname));
614 if (path != "." && path != "./" && !path.empty()) {
615 buf = os::slashify_path(path);
616 if (!suffixIs(path, '/'))
620 return buf + basename;
624 // Strips path from filename
625 string const OnlyFilename(string const & fname)
630 string::size_type j = fname.rfind('/');
631 if (j == string::npos) // no '/' in fname
635 return fname.substr(j + 1);
639 /// Returns true is path is absolute
640 bool AbsolutePath(string const & path)
642 return os::is_absolute_path(path);
647 // Create absolute path. If impossible, don't do anything
648 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
649 string const ExpandPath(string const & path)
651 // checks for already absolute path
652 string RTemp(ReplaceEnvironmentPath(path));
653 if (os::is_absolute_path(RTemp))
657 string const copy(RTemp);
660 RTemp = split(RTemp, Temp, '/');
663 return lyx::getcwd() /*GetCWD()*/ + '/' + RTemp;
666 return GetEnvPath("HOME") + '/' + RTemp;
669 return MakeAbsPath(copy);
671 // Don't know how to handle this
677 // Constracts path/../path
678 // Can't handle "../../" or "/../" (Asger)
679 string const NormalizePath(string const & path)
685 if (os::is_absolute_path(path))
688 // Make implicit current directory explicit
691 while (!RTemp.empty()) {
693 RTemp = split(RTemp, Temp, '/');
697 } else if (Temp == "..") {
698 // Remove one level of TempBase
699 string::difference_type i = TempBase.length() - 2;
700 while (i > 0 && TempBase[i] != '/')
702 if (i >= 0 && TempBase[i] == '/')
703 TempBase.erase(i + 1, string::npos);
707 TempBase += Temp + '/';
711 // returns absolute path
716 string const GetFileContents(string const & fname)
718 FileInfo finfo(fname);
720 ifstream ifs(fname.c_str());
725 return STRCONV(ofs.str());
728 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
734 // Search ${...} as Variable-Name inside the string and replace it with
735 // the denoted environmentvariable
736 // Allow Variables according to
737 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
740 string const ReplaceEnvironmentPath(string const & path)
743 // CompareChar: Environment variables starts with this character
744 // PathChar: Next path component start with this character
745 // while CompareChar found do:
746 // Split String with PathChar
747 // Search Environmentvariable
748 // if found: Replace Strings
750 char const CompareChar = '$';
751 char const FirstChar = '{';
752 char const EndChar = '}';
753 char const UnderscoreChar = '_';
754 string EndString; EndString += EndChar;
755 string FirstString; FirstString += FirstChar;
756 string CompareString; CompareString += CompareChar;
757 string const RegExp("*}*"); // Exist EndChar inside a String?
759 // first: Search for a '$' - Sign.
761 string result1; //(copy); // for split-calls
762 string result0 = split(path, result1, CompareChar);
763 while (!result0.empty()) {
764 string copy1(result0); // contains String after $
766 // Check, if there is an EndChar inside original String.
768 if (!regexMatch(copy1, RegExp)) {
769 // No EndChar inside. So we are finished
770 result1 += CompareString + result0;
776 string res0 = split(copy1, res1, EndChar);
777 // Now res1 holds the environmentvariable
778 // First, check, if Contents is ok.
779 if (res1.empty()) { // No environmentvariable. Continue Loop.
780 result1 += CompareString + FirstString;
784 // check contents of res1
785 char const * res1_contents = res1.c_str();
786 if (*res1_contents != FirstChar) {
787 // Again No Environmentvariable
788 result1 += CompareString;
792 // Check for variable names
793 // Situation ${} is detected as "No Environmentvariable"
794 char const * cp1 = res1_contents + 1;
795 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
797 while (*cp1 && result) {
798 result = isalnum(*cp1) ||
799 (*cp1 == UnderscoreChar);
804 // no correct variable name
805 result1 += CompareString + res1 + EndString;
806 result0 = split(res0, res1, CompareChar);
811 string env(GetEnv(res1_contents + 1));
813 // Congratulations. Environmentvariable found
816 result1 += CompareString + res1 + EndString;
819 result0 = split(res0, res1, CompareChar);
826 // Make relative path out of two absolute paths
827 string const MakeRelPath(string const & abspath, string const & basepath)
828 // Makes relative path out of absolute path. If it is deeper than basepath,
829 // it's easy. If basepath and abspath share something (they are all deeper
830 // than some directory), it'll be rendered using ..'s. If they are completely
831 // different, then the absolute path will be used as relative path.
833 string::size_type const abslen = abspath.length();
834 string::size_type const baselen = basepath.length();
836 string::size_type i = os::common_path(abspath, basepath);
839 // actually no match - cannot make it relative
843 // Count how many dirs there are in basepath above match
844 // and append as many '..''s into relpath
846 string::size_type j = i;
847 while (j < baselen) {
848 if (basepath[j] == '/') {
849 if (j + 1 == baselen)
856 // Append relative stuff from common directory to abspath
857 if (abspath[i] == '/')
859 for (; i < abslen; ++i)
862 if (suffixIs(buf, '/'))
863 buf.erase(buf.length() - 1);
864 // Substitute empty with .
871 // Append sub-directory(ies) to a path in an intelligent way
872 string const AddPath(string const & path, string const & path_2)
875 string const path2 = os::slashify_path(path_2);
877 if (!path.empty() && path != "." && path != "./") {
878 buf = os::slashify_path(path);
879 if (path[path.length() - 1] != '/')
883 if (!path2.empty()) {
884 string::size_type const p2start = path2.find_first_not_of('/');
885 string::size_type const p2end = path2.find_last_not_of('/');
886 string const tmp = path2.substr(p2start, p2end - p2start + 1);
894 Change extension of oldname to extension.
895 Strips path off if no_path == true.
896 If no extension on oldname, just appends.
898 string const ChangeExtension(string const & oldname, string const & extension)
900 string::size_type const last_slash = oldname.rfind('/');
901 string::size_type last_dot = oldname.rfind('.');
902 if (last_dot < last_slash && last_slash != string::npos)
903 last_dot = string::npos;
906 // Make sure the extension starts with a dot
907 if (!extension.empty() && extension[0] != '.')
908 ext= '.' + extension;
912 return os::slashify_path(oldname.substr(0, last_dot) + ext);
916 /// Return the extension of the file (not including the .)
917 string const GetExtension(string const & name)
919 string::size_type const last_slash = name.rfind('/');
920 string::size_type const last_dot = name.rfind('.');
921 if (last_dot != string::npos &&
922 (last_slash == string::npos || last_dot > last_slash))
923 return name.substr(last_dot + 1,
924 name.length() - (last_dot + 1));
929 // the different filetypes and what they contain in one of the first lines
930 // (dots are any characters). (Herbert 20020131)
933 // EPS %!PS-Adobe-3.0 EPSF...
940 // PBM P1... or P4 (B/W)
941 // PGM P2... or P5 (Grayscale)
942 // PPM P3... or P6 (color)
943 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
944 // SGI \001\332... (decimal 474)
946 // TIFF II... or MM...
948 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
949 // ...static char *...
950 // XWD \000\000\000\151 (0x00006900) decimal 105
952 // GZIP \037\213\010\010... http://www.ietf.org/rfc/rfc1952.txt
953 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
954 // Z \037\177 UNIX compress
956 /// return the "extension" which belongs to the contents.
957 /// for no knowing contents return the extension. Without
958 /// an extension and unknown contents we return "user"
959 string const getExtFromContents(string const & filename)
962 if (filename.empty() || !IsFileReadable(filename))
966 ifstream ifs(filename.c_str());
968 // Couldn't open file...
972 string const gzipStamp = "\037\213\010\010";
975 string const zipStamp = "PK";
978 string const compressStamp = "\037\177";
980 // Maximum strings to read
981 int const max_count = 50;
985 bool firstLine = true;
986 while ((count++ < max_count) && format.empty()) {
988 lyxerr[Debug::GRAPHICS]
989 << "filetools(getExtFromContents)\n"
990 << "\tFile type not recognised before EOF!"
996 string const stamp = str.substr(0,2);
997 if (firstLine && str.size() >= 2) {
998 // at first we check for a zipped file, because this
999 // information is saved in the first bytes of the file!
1000 // also some graphic formats which save the information
1001 // in the first line, too.
1002 if (prefixIs(str, gzipStamp)) {
1005 } else if (stamp == zipStamp) {
1008 } else if (stamp == compressStamp) {
1009 format = "compress";
1011 // the graphics part
1012 } else if (stamp == "BM") {
1015 } else if (stamp == "\001\332") {
1019 // Don't need to use str.at(0), str.at(1) because
1020 // we already know that str.size() >= 2
1021 } else if (str[0] == 'P') {
1037 } else if ((stamp == "II") || (stamp == "MM")) {
1040 } else if (prefixIs(str,"%TGIF")) {
1043 } else if (prefixIs(str,"#FIG")) {
1046 } else if (prefixIs(str,"GIF")) {
1049 } else if (str.size() > 3) {
1050 int const c = ((str[0] << 24) & (str[1] << 16) &
1051 (str[2] << 8) & str[3]);
1060 if (!format.empty())
1062 else if (contains(str,"EPSF"))
1063 // dummy, if we have wrong file description like
1064 // %!PS-Adobe-2.0EPSF"
1067 else if (contains(str,"Grace"))
1070 else if (contains(str,"JFIF"))
1073 else if (contains(str,"%PDF"))
1076 else if (contains(str,"PNG"))
1079 else if (contains(str,"%!PS-Adobe")) {
1082 if (contains(str,"EPSF"))
1088 else if (contains(str,"_bits[]"))
1091 else if (contains(str,"XPM") || contains(str, "static char *"))
1094 else if (contains(str,"BITPIX"))
1098 if (!format.empty()) {
1099 lyxerr[Debug::GRAPHICS]
1100 << "Recognised Fileformat: " << format << endl;
1104 string const ext(GetExtension(filename));
1105 lyxerr[Debug::GRAPHICS]
1106 << "filetools(getExtFromContents)\n"
1107 << "\tCouldn't find a known Type!\n";
1109 lyxerr[Debug::GRAPHICS]
1110 << "\twill take the file extension -> "
1114 lyxerr[Debug::GRAPHICS]
1115 << "\twill use ext or a \"user\" defined format" << endl;
1121 /// check for zipped file
1122 bool zippedFile(string const & name)
1124 string const type = getExtFromContents(name);
1125 if (contains("gzip zip compress", type) && !type.empty())
1131 string const unzipFile(string const & zipped_file)
1133 string const file = ChangeExtension(zipped_file, string());
1134 string const tempfile = lyx::tempName(string(), file);
1136 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1138 one.startscript(Systemcall::Wait, command);
1139 // test that command was executed successfully (anon)
1140 // yes, please do. (Lgb)
1145 string const MakeDisplayPath(string const & path, unsigned int threshold)
1149 string const home(GetEnvPath("HOME"));
1151 // replace /home/blah with ~/
1152 if (prefixIs(str, home))
1153 str = subst(str, home, "~");
1155 if (str.length() <= threshold)
1158 string const prefix = ".../";
1161 while (str.length() > threshold)
1162 str = split(str, temp, '/');
1164 // Did we shorten everything away?
1166 // Yes, filename itself is too long.
1167 // Pick the start and the end of the filename.
1168 str = OnlyFilename(path);
1169 string const head = str.substr(0, threshold / 2 - 3);
1171 string::size_type len = str.length();
1173 str.substr(len - threshold / 2 - 2, len - 1);
1174 str = head + "..." + tail;
1177 return prefix + str;
1181 bool LyXReadLink(string const & file, string & link, bool resolve)
1183 char linkbuffer[512];
1184 // Should be PATH_MAX but that needs autconf support
1185 int const nRead = ::readlink(file.c_str(),
1186 linkbuffer, sizeof(linkbuffer) - 1);
1189 linkbuffer[nRead] = '\0'; // terminator
1191 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1198 cmd_ret const RunCommand(string const & cmd)
1200 // One question is if we should use popen or
1201 // create our own popen based on fork, exec, pipe
1202 // of course the best would be to have a
1203 // pstream (process stream), with the
1204 // variants ipstream, opstream
1206 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1208 // (Claus Hentschel) Check if popen was succesful ;-)
1210 return make_pair(-1, string());
1215 ret += static_cast<char>(c);
1218 int const pret = pclose(inf);
1219 return make_pair(pret, ret);
1223 string const findtexfile(string const & fil, string const & /*format*/)
1225 /* There is no problem to extend this function too use other
1226 methods to look for files. It could be setup to look
1227 in environment paths and also if wanted as a last resort
1228 to a recursive find. One of the easier extensions would
1229 perhaps be to use the LyX file lookup methods. But! I am
1230 going to implement this until I see some demand for it.
1234 // If the file can be found directly, we just return a
1235 // absolute path version of it.
1236 if (FileInfo(fil).exist())
1237 return MakeAbsPath(fil);
1239 // No we try to find it using kpsewhich.
1240 // It seems from the kpsewhich manual page that it is safe to use
1241 // kpsewhich without --format: "When the --format option is not
1242 // given, the search path used when looking for a file is inferred
1243 // from the name given, by looking for a known extension. If no
1244 // known extension is found, the search path for TeX source files
1246 // However, we want to take advantage of the format sine almost all
1247 // the different formats has environment variables that can be used
1248 // to controll which paths to search. f.ex. bib looks in
1249 // BIBINPUTS and TEXBIB. Small list follows:
1250 // bib - BIBINPUTS, TEXBIB
1252 // graphic/figure - TEXPICTS, TEXINPUTS
1253 // ist - TEXINDEXSTYLE, INDEXSTYLE
1254 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1256 // tfm - TFMFONTS, TEXFONTS
1257 // This means that to use kpsewhich in the best possible way we
1258 // should help it by setting additional path in the approp. envir.var.
1259 string const kpsecmd = "kpsewhich " + fil;
1261 cmd_ret const c = RunCommand(kpsecmd);
1263 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1264 << "kpse result = `" << rtrim(c.second, "\n")
1267 return os::internal_path(rtrim(c.second, "\n\r"));
1273 void removeAutosaveFile(string const & filename)
1275 string a = OnlyPath(filename);
1277 a += OnlyFilename(filename);
1279 FileInfo const fileinfo(a);
1280 if (fileinfo.exist())
1285 void readBB_lyxerrMessage(string const & file, bool & zipped,
1286 string const & message)
1288 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1289 << message << std::endl;
1295 string const readBB_from_PSFile(string const & file)
1297 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1298 // It seems that every command in the header has an own line,
1299 // getline() should work for all files.
1300 // On the other hand some plot programs write the bb at the
1301 // end of the file. Than we have in the header:
1302 // %%BoundingBox: (atend)
1303 // In this case we must check the end.
1304 bool zipped = zippedFile(file);
1305 string const file_ = zipped ?
1306 string(unzipFile(file)) : string(file);
1307 string const format = getExtFromContents(file_);
1309 if (format != "eps" && format != "ps") {
1310 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1314 std::ifstream is(file_.c_str());
1318 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1319 string const bb = ltrim(s.substr(14));
1320 readBB_lyxerrMessage(file_, zipped, bb);
1324 readBB_lyxerrMessage(file_, zipped, "no bb found");
1329 string copyFileToDir(string const & path, string const & file_in)
1331 lyx::Assert(AbsolutePath(path));
1333 // First, make the file path relative to path.
1334 string file_out = MakeRelPath(path, NormalizePath(file_in));
1335 file_out = os::slashify_path(file_out);
1337 // Now generate a unique filename.
1338 // Remove the extension.
1339 file_out = ChangeExtension(file_out, string());
1340 // Replace '/' in the file name with '_'
1341 file_out = subst(file_out, "/", "_");
1342 // Replace '.' in the file name with '_'
1343 file_out = subst(file_out, ".", "_");
1344 // Append a unique ID
1346 file_out += '_' + tostr(id++);
1347 // Add the extension back on
1348 file_out = ChangeExtension(file_out, GetExtension(file_in));
1349 // Put this file in the buffer's temp dir
1350 file_out = MakeAbsPath(file_out, path);
1352 // If the original is newer than the copy, then copy the original
1353 // to the new directory.
1354 FileInfo fi(file_in);
1355 FileInfo fi2(file_out);
1357 bool success = true;
1360 difftime(fi.getModificationTime(),
1361 fi2.getModificationTime()) >= 0)
1362 success = lyx::copy(file_in, file_out);
1365 return success ? file_out : string();