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
17 * Full author contact details are available in file CREDITS.
19 * General path-mangling functions
25 #include "support/tostr.h"
26 #include "support/systemcall.h"
28 #include "filetools.h"
32 #include "path_defines.h"
37 #include "support/std_sstream.h"
39 #include <boost/assert.hpp>
40 #include <boost/regex.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
81 using std::ostringstream;
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 // the following comments are from intl/dcigettext.c. We try
313 // to mimick this behaviour here.
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. */
317 /* [Otherwise] We have to proceed with the POSIX methods of
318 looking to `LC_ALL', `LC_xxx', and `LANG'. On some systems
319 this can be done by the `setlocale' function itself. */
321 #if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL
322 lang = setlocale(LC_MESSAGES, NULL);
324 string lang = GetEnv("LC_ALL");
326 lang = GetEnv("LC_MESSAGES");
328 lang = GetEnv("LANG");
335 string const language = GetEnv("LANGUAGE");
336 if (lang != "C" && !language.empty())
339 lang = token(lang, '_', 0);
341 if (lang.empty() || lang == "C")
342 return LibFileSearch(dir, name, ext);
344 string const tmp = LibFileSearch(dir, lang + '_' + name,
349 return LibFileSearch(dir, name, ext);
354 string const LibScriptSearch(string const & command_in)
356 string const token_scriptpath("$$s/");
358 string command = command_in;
359 // Find the starting position of "$$s/"
360 string::size_type const pos1 = command.find(token_scriptpath);
361 if (pos1 == string::npos)
363 // Find the end of the "$$s/some_script" word within command
364 string::size_type const start_script = pos1 + 4;
365 string::size_type const pos2 = command.find(' ', start_script);
366 string::size_type const size_script = pos2 == string::npos?
367 (command.size() - start_script) : pos2 - start_script;
369 // Does this script file exist?
370 string const script =
371 LibFileSearch("scripts", command.substr(start_script, size_script));
373 if (script.empty()) {
374 // Replace "$$s/" with ""
375 command.erase(pos1, 4);
377 // Replace "$$s/some_script" with "$LYX_SCRIPT_PATH/some_script"
378 string::size_type const size_replace = size_script + 4;
379 command.replace(pos1, size_replace, script);
386 string const GetEnv(string const & envname)
388 // f.ex. what about error checking?
389 char const * const ch = getenv(envname.c_str());
390 string const envstr = !ch ? "" : ch;
395 string const GetEnvPath(string const & name)
398 string const pathlist = subst(GetEnv(name), ':', ';');
400 string const pathlist = os::slashify_path(GetEnv(name));
402 return rtrim(pathlist, ";");
408 int DeleteAllFilesInDir(string const & path)
410 // I have decided that we will be using parts from the boost
411 // library. Check out http://www.boost.org/
412 // For directory access we will then use the directory_iterator.
413 // Then the code will be something like:
414 // directory_iterator dit(path);
415 // directory_iterator dend;
416 // if (dit == dend) {
419 // for (; dit != dend; ++dit) {
420 // string filename(*dit);
421 // if (filename == "." || filename == "..")
423 // string unlinkpath(AddName(path, filename));
424 // lyx::unlink(unlinkpath);
427 DIR * dir = ::opendir(path.c_str());
432 int return_value = 0;
433 while ((de = readdir(dir))) {
434 string const temp = de->d_name;
435 if (temp == "." || temp == "..")
437 string const unlinkpath = AddName (path, temp);
439 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
443 FileInfo fi(unlinkpath);
444 if (fi.isOK() && fi.isDir())
445 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
446 deleted &= (unlink(unlinkpath) == 0);
455 string const createTmpDir(string const & tempdir, string const & mask)
458 << "createTmpDir: tempdir=`" << tempdir << "'\n"
459 << "createTmpDir: mask=`" << mask << '\'' << endl;
461 string const tmpfl(tempName(tempdir, mask));
462 // lyx::tempName actually creates a file to make sure that it
463 // stays unique. So we have to delete it before we can create
464 // a dir with the same name. Note also that we are not thread
465 // safe because of the gap between unlink and mkdir. (Lgb)
468 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
469 lyxerr << "LyX could not create the temporary directory '"
470 << tmpfl << "'" << endl;
474 return MakeAbsPath(tmpfl);
480 int destroyDir(string const & tmpdir)
483 Path p(user_lyxdir());
485 if (DeleteAllFilesInDir(tmpdir))
495 string const createBufferTmpDir()
498 // We are in our own directory. Why bother to mangle name?
499 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
501 string const tmpfl = os::getTmpDir() + "/lyx_tmpbuf" + tostr(count++);
502 if (mkdir(tmpfl, 0777)) {
503 lyxerr << "LyX could not create the temporary directory '"
504 << tmpfl << "'" << endl;
511 string const createLyXTmpDir(string const & deflt)
513 if (!deflt.empty() && deflt != "/tmp") {
514 if (mkdir(deflt, 0777)) {
515 if (IsDirWriteable(deflt))
516 // deflt could not be created because it
517 // did exist already, so let's create our own
520 Path p(user_lyxdir());
522 return createTmpDir(deflt, "lyx_tmpdir");
524 // some other error occured.
526 Path p(user_lyxdir());
528 return createTmpDir("/tmp", "lyx_tmpdir");
533 Path p(user_lyxdir());
535 return createTmpDir("/tmp", "lyx_tmpdir");
540 bool createDirectory(string const & path, int permission)
542 string temp(rtrim(os::slashify_path(path), "/"));
544 BOOST_ASSERT(!temp.empty());
546 if (mkdir(temp, permission))
553 // Strip filename from path name
554 string const OnlyPath(string const & Filename)
556 // If empty filename, return empty
557 if (Filename.empty()) return Filename;
559 // Find last / or start of filename
560 string::size_type j = Filename.rfind('/');
561 if (j == string::npos)
563 return Filename.substr(0, j + 1);
567 // Convert relative path into absolute path based on a basepath.
568 // If relpath is absolute, just use that.
569 // If basepath is empty, use CWD as base.
570 string const MakeAbsPath(string const & RelPath, string const & BasePath)
572 // checks for already absolute path
573 if (os::is_absolute_path(RelPath))
576 // Copies given paths
577 string TempRel(os::slashify_path(RelPath));
578 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
579 TempRel = subst(TempRel, "//", "/");
583 if (os::is_absolute_path(BasePath))
586 TempBase = AddPath(getcwd(), BasePath);
588 // Handle /./ at the end of the path
589 while (suffixIs(TempBase, "/./"))
590 TempBase.erase(TempBase.length() - 2);
592 // processes relative path
593 string RTemp(TempRel);
596 while (!RTemp.empty()) {
598 RTemp = split(RTemp, Temp, '/');
600 if (Temp == ".") continue;
602 // Remove one level of TempBase
603 string::difference_type i = TempBase.length() - 2;
606 while (i > 0 && TempBase[i] != '/') --i;
610 while (i > 2 && TempBase[i] != '/') --i;
613 TempBase.erase(i, string::npos);
616 } else if (Temp.empty() && !RTemp.empty()) {
617 TempBase = os::current_root() + RTemp;
620 // Add this piece to TempBase
621 if (!suffixIs(TempBase, '/'))
627 // returns absolute path
628 return os::slashify_path(TempBase);
632 // Correctly append filename to the pathname.
633 // If pathname is '.', then don't use pathname.
634 // Chops any path of filename.
635 string const AddName(string const & path, string const & fname)
638 string const basename(OnlyFilename(fname));
642 if (path != "." && path != "./" && !path.empty()) {
643 buf = os::slashify_path(path);
644 if (!suffixIs(path, '/'))
648 return buf + basename;
652 // Strips path from filename
653 string const OnlyFilename(string const & fname)
658 string::size_type j = fname.rfind('/');
659 if (j == string::npos) // no '/' in fname
663 return fname.substr(j + 1);
667 /// Returns true is path is absolute
668 bool AbsolutePath(string const & path)
670 return os::is_absolute_path(path);
675 // Create absolute path. If impossible, don't do anything
676 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
677 string const ExpandPath(string const & path)
679 // checks for already absolute path
680 string RTemp(ReplaceEnvironmentPath(path));
681 if (os::is_absolute_path(RTemp))
685 string const copy(RTemp);
688 RTemp = split(RTemp, Temp, '/');
691 return getcwd() + '/' + RTemp;
694 return GetEnvPath("HOME") + '/' + RTemp;
697 return MakeAbsPath(copy);
699 // Don't know how to handle this
705 // Constracts path/../path
706 // Can't handle "../../" or "/../" (Asger)
707 // Also converts paths like /foo//bar ==> /foo/bar
708 string const NormalizePath(string const & path)
714 if (os::is_absolute_path(path))
717 // Make implicit current directory explicit
720 // Normalise paths like /foo//bar ==> /foo/bar
721 boost::RegEx regex("/{2,}");
722 RTemp = regex.Merge(RTemp, "/");
724 while (!RTemp.empty()) {
726 RTemp = split(RTemp, Temp, '/');
730 } else if (Temp == "..") {
731 // Remove one level of TempBase
732 string::difference_type i = TempBase.length() - 2;
733 while (i > 0 && TempBase[i] != '/')
735 if (i >= 0 && TempBase[i] == '/')
736 TempBase.erase(i + 1, string::npos);
740 TempBase += Temp + '/';
744 // returns absolute path
749 string const GetFileContents(string const & fname)
751 FileInfo finfo(fname);
753 ifstream ifs(fname.c_str());
761 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
766 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
767 string const ReplaceEnvironmentPath(string const & path)
769 // ${VAR} is defined as
770 // $\{[A-Za-z_][A-Za-z_0-9]*\}
771 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
773 // $VAR is defined as:
774 // $\{[A-Za-z_][A-Za-z_0-9]*\}
775 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
777 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
778 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
781 string result = path;
783 regex_match(result, what, envvar_br_re);
784 if (!what[0].matched) {
785 regex_match(result, what, envvar_re);
786 if (!what[0].matched)
789 result = what.str(1) + GetEnv(what.str(2)) + what.str(3);
795 // Make relative path out of two absolute paths
796 string const MakeRelPath(string const & abspath, string const & basepath)
797 // Makes relative path out of absolute path. If it is deeper than basepath,
798 // it's easy. If basepath and abspath share something (they are all deeper
799 // than some directory), it'll be rendered using ..'s. If they are completely
800 // different, then the absolute path will be used as relative path.
802 string::size_type const abslen = abspath.length();
803 string::size_type const baselen = basepath.length();
805 string::size_type i = os::common_path(abspath, basepath);
808 // actually no match - cannot make it relative
812 // Count how many dirs there are in basepath above match
813 // and append as many '..''s into relpath
815 string::size_type j = i;
816 while (j < baselen) {
817 if (basepath[j] == '/') {
818 if (j + 1 == baselen)
825 // Append relative stuff from common directory to abspath
826 if (abspath[i] == '/')
828 for (; i < abslen; ++i)
831 if (suffixIs(buf, '/'))
832 buf.erase(buf.length() - 1);
833 // Substitute empty with .
840 // Append sub-directory(ies) to a path in an intelligent way
841 string const AddPath(string const & path, string const & path_2)
844 string const path2 = os::slashify_path(path_2);
846 if (!path.empty() && path != "." && path != "./") {
847 buf = os::slashify_path(path);
848 if (path[path.length() - 1] != '/')
852 if (!path2.empty()) {
853 string::size_type const p2start = path2.find_first_not_of('/');
854 string::size_type const p2end = path2.find_last_not_of('/');
855 string const tmp = path2.substr(p2start, p2end - p2start + 1);
863 Change extension of oldname to extension.
864 Strips path off if no_path == true.
865 If no extension on oldname, just appends.
867 string const ChangeExtension(string const & oldname, string const & extension)
869 string::size_type const last_slash = oldname.rfind('/');
870 string::size_type last_dot = oldname.rfind('.');
871 if (last_dot < last_slash && last_slash != string::npos)
872 last_dot = string::npos;
875 // Make sure the extension starts with a dot
876 if (!extension.empty() && extension[0] != '.')
877 ext= '.' + extension;
881 return os::slashify_path(oldname.substr(0, last_dot) + ext);
885 /// Return the extension of the file (not including the .)
886 string const GetExtension(string const & name)
888 string::size_type const last_slash = name.rfind('/');
889 string::size_type const last_dot = name.rfind('.');
890 if (last_dot != string::npos &&
891 (last_slash == string::npos || last_dot > last_slash))
892 return name.substr(last_dot + 1,
893 name.length() - (last_dot + 1));
898 // the different filetypes and what they contain in one of the first lines
899 // (dots are any characters). (Herbert 20020131)
902 // EPS %!PS-Adobe-3.0 EPSF...
909 // PBM P1... or P4 (B/W)
910 // PGM P2... or P5 (Grayscale)
911 // PPM P3... or P6 (color)
912 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
913 // SGI \001\332... (decimal 474)
915 // TIFF II... or MM...
917 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
918 // ...static char *...
919 // XWD \000\000\000\151 (0x00006900) decimal 105
921 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
922 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
923 // Z \037\235 UNIX compress
925 /// return the "extension" which belongs to the contents.
926 /// for no knowing contents return the extension. Without
927 /// an extension and unknown contents we return "user"
928 string const getExtFromContents(string const & filename)
931 if (filename.empty() || !IsFileReadable(filename))
935 ifstream ifs(filename.c_str());
937 // Couldn't open file...
941 string const gzipStamp = "\037\213";
944 string const zipStamp = "PK";
947 string const compressStamp = "\037\235";
949 // Maximum strings to read
950 int const max_count = 50;
954 bool firstLine = true;
955 while ((count++ < max_count) && format.empty()) {
957 lyxerr[Debug::GRAPHICS]
958 << "filetools(getExtFromContents)\n"
959 << "\tFile type not recognised before EOF!"
965 string const stamp = str.substr(0,2);
966 if (firstLine && str.size() >= 2) {
967 // at first we check for a zipped file, because this
968 // information is saved in the first bytes of the file!
969 // also some graphic formats which save the information
970 // in the first line, too.
971 if (prefixIs(str, gzipStamp)) {
974 } else if (stamp == zipStamp) {
977 } else if (stamp == compressStamp) {
981 } else if (stamp == "BM") {
984 } else if (stamp == "\001\332") {
988 // Don't need to use str.at(0), str.at(1) because
989 // we already know that str.size() >= 2
990 } else if (str[0] == 'P') {
1006 } else if ((stamp == "II") || (stamp == "MM")) {
1009 } else if (prefixIs(str,"%TGIF")) {
1012 } else if (prefixIs(str,"#FIG")) {
1015 } else if (prefixIs(str,"GIF")) {
1018 } else if (str.size() > 3) {
1019 int const c = ((str[0] << 24) & (str[1] << 16) &
1020 (str[2] << 8) & str[3]);
1029 if (!format.empty())
1031 else if (contains(str,"EPSF"))
1032 // dummy, if we have wrong file description like
1033 // %!PS-Adobe-2.0EPSF"
1036 else if (contains(str,"Grace"))
1039 else if (contains(str,"JFIF"))
1042 else if (contains(str,"%PDF"))
1045 else if (contains(str,"PNG"))
1048 else if (contains(str,"%!PS-Adobe")) {
1051 if (contains(str,"EPSF"))
1057 else if (contains(str,"_bits[]"))
1060 else if (contains(str,"XPM") || contains(str, "static char *"))
1063 else if (contains(str,"BITPIX"))
1067 if (!format.empty()) {
1068 lyxerr[Debug::GRAPHICS]
1069 << "Recognised Fileformat: " << format << endl;
1073 string const ext(GetExtension(filename));
1074 lyxerr[Debug::GRAPHICS]
1075 << "filetools(getExtFromContents)\n"
1076 << "\tCouldn't find a known Type!\n";
1078 lyxerr[Debug::GRAPHICS]
1079 << "\twill take the file extension -> "
1083 lyxerr[Debug::GRAPHICS]
1084 << "\twill use ext or a \"user\" defined format" << endl;
1090 /// check for zipped file
1091 bool zippedFile(string const & name)
1093 string const type = getExtFromContents(name);
1094 if (contains("gzip zip compress", type) && !type.empty())
1100 string const unzippedFileName(string const & zipped_file)
1102 string const ext = GetExtension(zipped_file);
1103 if (ext == "gz" || ext == "z" || ext == "Z")
1104 return ChangeExtension(zipped_file, string());
1105 return "unzipped_" + zipped_file;
1109 string const unzipFile(string const & zipped_file, string const & unzipped_file)
1111 string const tempfile = unzipped_file.empty() ?
1112 unzippedFileName(zipped_file) : unzipped_file;
1114 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1116 one.startscript(Systemcall::Wait, command);
1117 // test that command was executed successfully (anon)
1118 // yes, please do. (Lgb)
1123 string const MakeDisplayPath(string const & path, unsigned int threshold)
1127 string const home(GetEnvPath("HOME"));
1129 // replace /home/blah with ~/
1130 if (prefixIs(str, home))
1131 str = subst(str, home, "~");
1133 if (str.length() <= threshold)
1136 string const prefix = ".../";
1139 while (str.length() > threshold)
1140 str = split(str, temp, '/');
1142 // Did we shorten everything away?
1144 // Yes, filename itself is too long.
1145 // Pick the start and the end of the filename.
1146 str = OnlyFilename(path);
1147 string const head = str.substr(0, threshold / 2 - 3);
1149 string::size_type len = str.length();
1151 str.substr(len - threshold / 2 - 2, len - 1);
1152 str = head + "..." + tail;
1155 return prefix + str;
1159 bool LyXReadLink(string const & file, string & link, bool resolve)
1161 char linkbuffer[512];
1162 // Should be PATH_MAX but that needs autconf support
1163 int const nRead = ::readlink(file.c_str(),
1164 linkbuffer, sizeof(linkbuffer) - 1);
1167 linkbuffer[nRead] = '\0'; // terminator
1169 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1176 cmd_ret const RunCommand(string const & cmd)
1178 // One question is if we should use popen or
1179 // create our own popen based on fork, exec, pipe
1180 // of course the best would be to have a
1181 // pstream (process stream), with the
1182 // variants ipstream, opstream
1184 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1186 // (Claus Hentschel) Check if popen was succesful ;-)
1188 return make_pair(-1, string());
1193 ret += static_cast<char>(c);
1196 int const pret = pclose(inf);
1197 return make_pair(pret, ret);
1201 string const findtexfile(string const & fil, string const & /*format*/)
1203 /* There is no problem to extend this function too use other
1204 methods to look for files. It could be setup to look
1205 in environment paths and also if wanted as a last resort
1206 to a recursive find. One of the easier extensions would
1207 perhaps be to use the LyX file lookup methods. But! I am
1208 going to implement this until I see some demand for it.
1212 // If the file can be found directly, we just return a
1213 // absolute path version of it.
1214 if (FileInfo(fil).exist())
1215 return MakeAbsPath(fil);
1217 // No we try to find it using kpsewhich.
1218 // It seems from the kpsewhich manual page that it is safe to use
1219 // kpsewhich without --format: "When the --format option is not
1220 // given, the search path used when looking for a file is inferred
1221 // from the name given, by looking for a known extension. If no
1222 // known extension is found, the search path for TeX source files
1224 // However, we want to take advantage of the format sine almost all
1225 // the different formats has environment variables that can be used
1226 // to controll which paths to search. f.ex. bib looks in
1227 // BIBINPUTS and TEXBIB. Small list follows:
1228 // bib - BIBINPUTS, TEXBIB
1230 // graphic/figure - TEXPICTS, TEXINPUTS
1231 // ist - TEXINDEXSTYLE, INDEXSTYLE
1232 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1234 // tfm - TFMFONTS, TEXFONTS
1235 // This means that to use kpsewhich in the best possible way we
1236 // should help it by setting additional path in the approp. envir.var.
1237 string const kpsecmd = "kpsewhich " + fil;
1239 cmd_ret const c = RunCommand(kpsecmd);
1241 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1242 << "kpse result = `" << rtrim(c.second, "\n")
1245 return os::internal_path(rtrim(c.second, "\n\r"));
1251 void removeAutosaveFile(string const & filename)
1253 string a = OnlyPath(filename);
1255 a += OnlyFilename(filename);
1257 FileInfo const fileinfo(a);
1258 if (fileinfo.exist())
1263 void readBB_lyxerrMessage(string const & file, bool & zipped,
1264 string const & message)
1266 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1267 << message << std::endl;
1268 #warning Why is this func deleting a file? (Lgb)
1274 string const readBB_from_PSFile(string const & file)
1276 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1277 // It seems that every command in the header has an own line,
1278 // getline() should work for all files.
1279 // On the other hand some plot programs write the bb at the
1280 // end of the file. Than we have in the header:
1281 // %%BoundingBox: (atend)
1282 // In this case we must check the end.
1283 bool zipped = zippedFile(file);
1284 string const file_ = zipped ?
1285 string(unzipFile(file)) : string(file);
1286 string const format = getExtFromContents(file_);
1288 if (format != "eps" && format != "ps") {
1289 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1293 std::ifstream is(file_.c_str());
1297 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1298 string const bb = ltrim(s.substr(14));
1299 readBB_lyxerrMessage(file_, zipped, bb);
1303 readBB_lyxerrMessage(file_, zipped, "no bb found");
1308 int compare_timestamps(string const & file1, string const & file2)
1310 BOOST_ASSERT(AbsolutePath(file1) && AbsolutePath(file2));
1312 // If the original is newer than the copy, then copy the original
1313 // to the new directory.
1318 if (f1.exist() && f2.exist()) {
1319 double const tmp = difftime(f1.getModificationTime(),
1320 f2.getModificationTime());
1322 cmp = tmp > 0 ? 1 : -1;
1324 } else if (f1.exist()) {
1326 } else if (f2.exist()) {
1333 } //namespace support