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'. */
320 string lang = GetEnv("LC_ALL");
322 lang = GetEnv("LC_MESSAGES");
324 lang = GetEnv("LANG");
330 string const language = GetEnv("LANGUAGE");
331 if (lang != "C" && lang != "POSIX" && !language.empty())
334 lang = token(lang, '_', 0);
336 if (lang.empty() || lang == "C")
337 return LibFileSearch(dir, name, ext);
339 string const tmp = LibFileSearch(dir, lang + '_' + name,
344 return LibFileSearch(dir, name, ext);
349 string const LibScriptSearch(string const & command_in)
351 string const token_scriptpath("$$s/");
353 string command = command_in;
354 // Find the starting position of "$$s/"
355 string::size_type const pos1 = command.find(token_scriptpath);
356 if (pos1 == string::npos)
358 // Find the end of the "$$s/some_script" word within command
359 string::size_type const start_script = pos1 + 4;
360 string::size_type const pos2 = command.find(' ', start_script);
361 string::size_type const size_script = pos2 == string::npos?
362 (command.size() - start_script) : pos2 - start_script;
364 // Does this script file exist?
365 string const script =
366 LibFileSearch("scripts", command.substr(start_script, size_script));
368 if (script.empty()) {
369 // Replace "$$s/" with ""
370 command.erase(pos1, 4);
372 // Replace "$$s/some_script" with "$LYX_SCRIPT_PATH/some_script"
373 string::size_type const size_replace = size_script + 4;
374 command.replace(pos1, size_replace, script);
381 string const GetEnv(string const & envname)
383 // f.ex. what about error checking?
384 char const * const ch = getenv(envname.c_str());
385 string const envstr = !ch ? "" : ch;
390 string const GetEnvPath(string const & name)
393 string const pathlist = subst(GetEnv(name), ':', ';');
395 string const pathlist = os::slashify_path(GetEnv(name));
397 return rtrim(pathlist, ";");
403 int DeleteAllFilesInDir(string const & path)
405 // I have decided that we will be using parts from the boost
406 // library. Check out http://www.boost.org/
407 // For directory access we will then use the directory_iterator.
408 // Then the code will be something like:
409 // directory_iterator dit(path);
410 // directory_iterator dend;
411 // if (dit == dend) {
414 // for (; dit != dend; ++dit) {
415 // string filename(*dit);
416 // if (filename == "." || filename == "..")
418 // string unlinkpath(AddName(path, filename));
419 // lyx::unlink(unlinkpath);
422 DIR * dir = ::opendir(path.c_str());
427 int return_value = 0;
428 while ((de = readdir(dir))) {
429 string const temp = de->d_name;
430 if (temp == "." || temp == "..")
432 string const unlinkpath = AddName (path, temp);
434 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
438 FileInfo fi(unlinkpath);
439 if (fi.isOK() && fi.isDir())
440 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
441 deleted &= (unlink(unlinkpath) == 0);
450 string const createTmpDir(string const & tempdir, string const & mask)
453 << "createTmpDir: tempdir=`" << tempdir << "'\n"
454 << "createTmpDir: mask=`" << mask << '\'' << endl;
456 string const tmpfl(tempName(tempdir, mask));
457 // lyx::tempName actually creates a file to make sure that it
458 // stays unique. So we have to delete it before we can create
459 // a dir with the same name. Note also that we are not thread
460 // safe because of the gap between unlink and mkdir. (Lgb)
463 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
464 lyxerr << "LyX could not create the temporary directory '"
465 << tmpfl << "'" << endl;
469 return MakeAbsPath(tmpfl);
475 int destroyDir(string const & tmpdir)
478 Path p(user_lyxdir());
480 if (DeleteAllFilesInDir(tmpdir))
490 string const createBufferTmpDir()
493 // We are in our own directory. Why bother to mangle name?
494 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
496 string const tmpfl = os::getTmpDir() + "/lyx_tmpbuf" + tostr(count++);
497 if (mkdir(tmpfl, 0777)) {
498 lyxerr << "LyX could not create the temporary directory '"
499 << tmpfl << "'" << endl;
506 string const createLyXTmpDir(string const & deflt)
508 if (!deflt.empty() && deflt != "/tmp") {
509 if (mkdir(deflt, 0777)) {
510 if (IsDirWriteable(deflt))
511 // deflt could not be created because it
512 // did exist already, so let's create our own
515 Path p(user_lyxdir());
517 return createTmpDir(deflt, "lyx_tmpdir");
519 // some other error occured.
521 Path p(user_lyxdir());
523 return createTmpDir("/tmp", "lyx_tmpdir");
528 Path p(user_lyxdir());
530 return createTmpDir("/tmp", "lyx_tmpdir");
535 bool createDirectory(string const & path, int permission)
537 string temp(rtrim(os::slashify_path(path), "/"));
539 BOOST_ASSERT(!temp.empty());
541 if (mkdir(temp, permission))
548 // Strip filename from path name
549 string const OnlyPath(string const & Filename)
551 // If empty filename, return empty
552 if (Filename.empty()) return Filename;
554 // Find last / or start of filename
555 string::size_type j = Filename.rfind('/');
556 if (j == string::npos)
558 return Filename.substr(0, j + 1);
562 // Convert relative path into absolute path based on a basepath.
563 // If relpath is absolute, just use that.
564 // If basepath is empty, use CWD as base.
565 string const MakeAbsPath(string const & RelPath, string const & BasePath)
567 // checks for already absolute path
568 if (os::is_absolute_path(RelPath))
571 // Copies given paths
572 string TempRel(os::slashify_path(RelPath));
573 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
574 TempRel = subst(TempRel, "//", "/");
578 if (os::is_absolute_path(BasePath))
581 TempBase = AddPath(getcwd(), BasePath);
583 // Handle /./ at the end of the path
584 while (suffixIs(TempBase, "/./"))
585 TempBase.erase(TempBase.length() - 2);
587 // processes relative path
588 string RTemp(TempRel);
591 while (!RTemp.empty()) {
593 RTemp = split(RTemp, Temp, '/');
595 if (Temp == ".") continue;
597 // Remove one level of TempBase
598 string::difference_type i = TempBase.length() - 2;
601 while (i > 0 && TempBase[i] != '/') --i;
605 while (i > 2 && TempBase[i] != '/') --i;
608 TempBase.erase(i, string::npos);
611 } else if (Temp.empty() && !RTemp.empty()) {
612 TempBase = os::current_root() + RTemp;
615 // Add this piece to TempBase
616 if (!suffixIs(TempBase, '/'))
622 // returns absolute path
623 return os::slashify_path(TempBase);
627 // Correctly append filename to the pathname.
628 // If pathname is '.', then don't use pathname.
629 // Chops any path of filename.
630 string const AddName(string const & path, string const & fname)
633 string const basename(OnlyFilename(fname));
637 if (path != "." && path != "./" && !path.empty()) {
638 buf = os::slashify_path(path);
639 if (!suffixIs(path, '/'))
643 return buf + basename;
647 // Strips path from filename
648 string const OnlyFilename(string const & fname)
653 string::size_type j = fname.rfind('/');
654 if (j == string::npos) // no '/' in fname
658 return fname.substr(j + 1);
662 /// Returns true is path is absolute
663 bool AbsolutePath(string const & path)
665 return os::is_absolute_path(path);
670 // Create absolute path. If impossible, don't do anything
671 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
672 string const ExpandPath(string const & path)
674 // checks for already absolute path
675 string RTemp(ReplaceEnvironmentPath(path));
676 if (os::is_absolute_path(RTemp))
680 string const copy(RTemp);
683 RTemp = split(RTemp, Temp, '/');
686 return getcwd() + '/' + RTemp;
689 return GetEnvPath("HOME") + '/' + RTemp;
692 return MakeAbsPath(copy);
694 // Don't know how to handle this
700 // Constracts path/../path
701 // Can't handle "../../" or "/../" (Asger)
702 // Also converts paths like /foo//bar ==> /foo/bar
703 string const NormalizePath(string const & path)
709 if (os::is_absolute_path(path))
712 // Make implicit current directory explicit
715 // Normalise paths like /foo//bar ==> /foo/bar
716 boost::RegEx regex("/{2,}");
717 RTemp = regex.Merge(RTemp, "/");
719 while (!RTemp.empty()) {
721 RTemp = split(RTemp, Temp, '/');
725 } else if (Temp == "..") {
726 // Remove one level of TempBase
727 string::difference_type i = TempBase.length() - 2;
728 while (i > 0 && TempBase[i] != '/')
730 if (i >= 0 && TempBase[i] == '/')
731 TempBase.erase(i + 1, string::npos);
735 TempBase += Temp + '/';
739 // returns absolute path
744 string const GetFileContents(string const & fname)
746 FileInfo finfo(fname);
748 ifstream ifs(fname.c_str());
756 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
761 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
762 string const ReplaceEnvironmentPath(string const & path)
764 // ${VAR} is defined as
765 // $\{[A-Za-z_][A-Za-z_0-9]*\}
766 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
768 // $VAR is defined as:
769 // $\{[A-Za-z_][A-Za-z_0-9]*\}
770 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
772 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
773 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
776 string result = path;
778 regex_match(result, what, envvar_br_re);
779 if (!what[0].matched) {
780 regex_match(result, what, envvar_re);
781 if (!what[0].matched)
784 result = what.str(1) + GetEnv(what.str(2)) + what.str(3);
790 // Make relative path out of two absolute paths
791 string const MakeRelPath(string const & abspath, string const & basepath)
792 // Makes relative path out of absolute path. If it is deeper than basepath,
793 // it's easy. If basepath and abspath share something (they are all deeper
794 // than some directory), it'll be rendered using ..'s. If they are completely
795 // different, then the absolute path will be used as relative path.
797 string::size_type const abslen = abspath.length();
798 string::size_type const baselen = basepath.length();
800 string::size_type i = os::common_path(abspath, basepath);
803 // actually no match - cannot make it relative
807 // Count how many dirs there are in basepath above match
808 // and append as many '..''s into relpath
810 string::size_type j = i;
811 while (j < baselen) {
812 if (basepath[j] == '/') {
813 if (j + 1 == baselen)
820 // Append relative stuff from common directory to abspath
821 if (abspath[i] == '/')
823 for (; i < abslen; ++i)
826 if (suffixIs(buf, '/'))
827 buf.erase(buf.length() - 1);
828 // Substitute empty with .
835 // Append sub-directory(ies) to a path in an intelligent way
836 string const AddPath(string const & path, string const & path_2)
839 string const path2 = os::slashify_path(path_2);
841 if (!path.empty() && path != "." && path != "./") {
842 buf = os::slashify_path(path);
843 if (path[path.length() - 1] != '/')
847 if (!path2.empty()) {
848 string::size_type const p2start = path2.find_first_not_of('/');
849 string::size_type const p2end = path2.find_last_not_of('/');
850 string const tmp = path2.substr(p2start, p2end - p2start + 1);
858 Change extension of oldname to extension.
859 Strips path off if no_path == true.
860 If no extension on oldname, just appends.
862 string const ChangeExtension(string const & oldname, string const & extension)
864 string::size_type const last_slash = oldname.rfind('/');
865 string::size_type last_dot = oldname.rfind('.');
866 if (last_dot < last_slash && last_slash != string::npos)
867 last_dot = string::npos;
870 // Make sure the extension starts with a dot
871 if (!extension.empty() && extension[0] != '.')
872 ext= '.' + extension;
876 return os::slashify_path(oldname.substr(0, last_dot) + ext);
880 /// Return the extension of the file (not including the .)
881 string const GetExtension(string const & name)
883 string::size_type const last_slash = name.rfind('/');
884 string::size_type const last_dot = name.rfind('.');
885 if (last_dot != string::npos &&
886 (last_slash == string::npos || last_dot > last_slash))
887 return name.substr(last_dot + 1,
888 name.length() - (last_dot + 1));
893 // the different filetypes and what they contain in one of the first lines
894 // (dots are any characters). (Herbert 20020131)
897 // EPS %!PS-Adobe-3.0 EPSF...
904 // PBM P1... or P4 (B/W)
905 // PGM P2... or P5 (Grayscale)
906 // PPM P3... or P6 (color)
907 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
908 // SGI \001\332... (decimal 474)
910 // TIFF II... or MM...
912 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
913 // ...static char *...
914 // XWD \000\000\000\151 (0x00006900) decimal 105
916 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
917 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
918 // Z \037\235 UNIX compress
920 /// return the "extension" which belongs to the contents.
921 /// for no knowing contents return the extension. Without
922 /// an extension and unknown contents we return "user"
923 string const getExtFromContents(string const & filename)
926 if (filename.empty() || !IsFileReadable(filename))
930 ifstream ifs(filename.c_str());
932 // Couldn't open file...
936 string const gzipStamp = "\037\213";
939 string const zipStamp = "PK";
942 string const compressStamp = "\037\235";
944 // Maximum strings to read
945 int const max_count = 50;
949 bool firstLine = true;
950 while ((count++ < max_count) && format.empty()) {
952 lyxerr[Debug::GRAPHICS]
953 << "filetools(getExtFromContents)\n"
954 << "\tFile type not recognised before EOF!"
960 string const stamp = str.substr(0,2);
961 if (firstLine && str.size() >= 2) {
962 // at first we check for a zipped file, because this
963 // information is saved in the first bytes of the file!
964 // also some graphic formats which save the information
965 // in the first line, too.
966 if (prefixIs(str, gzipStamp)) {
969 } else if (stamp == zipStamp) {
972 } else if (stamp == compressStamp) {
976 } else if (stamp == "BM") {
979 } else if (stamp == "\001\332") {
983 // Don't need to use str.at(0), str.at(1) because
984 // we already know that str.size() >= 2
985 } else if (str[0] == 'P') {
1001 } else if ((stamp == "II") || (stamp == "MM")) {
1004 } else if (prefixIs(str,"%TGIF")) {
1007 } else if (prefixIs(str,"#FIG")) {
1010 } else if (prefixIs(str,"GIF")) {
1013 } else if (str.size() > 3) {
1014 int const c = ((str[0] << 24) & (str[1] << 16) &
1015 (str[2] << 8) & str[3]);
1024 if (!format.empty())
1026 else if (contains(str,"EPSF"))
1027 // dummy, if we have wrong file description like
1028 // %!PS-Adobe-2.0EPSF"
1031 else if (contains(str,"Grace"))
1034 else if (contains(str,"JFIF"))
1037 else if (contains(str,"%PDF"))
1040 else if (contains(str,"PNG"))
1043 else if (contains(str,"%!PS-Adobe")) {
1046 if (contains(str,"EPSF"))
1052 else if (contains(str,"_bits[]"))
1055 else if (contains(str,"XPM") || contains(str, "static char *"))
1058 else if (contains(str,"BITPIX"))
1062 if (!format.empty()) {
1063 lyxerr[Debug::GRAPHICS]
1064 << "Recognised Fileformat: " << format << endl;
1068 string const ext(GetExtension(filename));
1069 lyxerr[Debug::GRAPHICS]
1070 << "filetools(getExtFromContents)\n"
1071 << "\tCouldn't find a known Type!\n";
1073 lyxerr[Debug::GRAPHICS]
1074 << "\twill take the file extension -> "
1078 lyxerr[Debug::GRAPHICS]
1079 << "\twill use ext or a \"user\" defined format" << endl;
1085 /// check for zipped file
1086 bool zippedFile(string const & name)
1088 string const type = getExtFromContents(name);
1089 if (contains("gzip zip compress", type) && !type.empty())
1095 string const unzippedFileName(string const & zipped_file)
1097 string const ext = GetExtension(zipped_file);
1098 if (ext == "gz" || ext == "z" || ext == "Z")
1099 return ChangeExtension(zipped_file, string());
1100 return "unzipped_" + zipped_file;
1104 string const unzipFile(string const & zipped_file, string const & unzipped_file)
1106 string const tempfile = unzipped_file.empty() ?
1107 unzippedFileName(zipped_file) : unzipped_file;
1109 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1111 one.startscript(Systemcall::Wait, command);
1112 // test that command was executed successfully (anon)
1113 // yes, please do. (Lgb)
1118 string const MakeDisplayPath(string const & path, unsigned int threshold)
1122 string const home(GetEnvPath("HOME"));
1124 // replace /home/blah with ~/
1125 if (prefixIs(str, home))
1126 str = subst(str, home, "~");
1128 if (str.length() <= threshold)
1131 string const prefix = ".../";
1134 while (str.length() > threshold)
1135 str = split(str, temp, '/');
1137 // Did we shorten everything away?
1139 // Yes, filename itself is too long.
1140 // Pick the start and the end of the filename.
1141 str = OnlyFilename(path);
1142 string const head = str.substr(0, threshold / 2 - 3);
1144 string::size_type len = str.length();
1146 str.substr(len - threshold / 2 - 2, len - 1);
1147 str = head + "..." + tail;
1150 return prefix + str;
1154 bool LyXReadLink(string const & file, string & link, bool resolve)
1156 char linkbuffer[512];
1157 // Should be PATH_MAX but that needs autconf support
1158 int const nRead = ::readlink(file.c_str(),
1159 linkbuffer, sizeof(linkbuffer) - 1);
1162 linkbuffer[nRead] = '\0'; // terminator
1164 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1171 cmd_ret const RunCommand(string const & cmd)
1173 // One question is if we should use popen or
1174 // create our own popen based on fork, exec, pipe
1175 // of course the best would be to have a
1176 // pstream (process stream), with the
1177 // variants ipstream, opstream
1179 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1181 // (Claus Hentschel) Check if popen was succesful ;-)
1183 return make_pair(-1, string());
1188 ret += static_cast<char>(c);
1191 int const pret = pclose(inf);
1192 return make_pair(pret, ret);
1196 string const findtexfile(string const & fil, string const & /*format*/)
1198 /* There is no problem to extend this function too use other
1199 methods to look for files. It could be setup to look
1200 in environment paths and also if wanted as a last resort
1201 to a recursive find. One of the easier extensions would
1202 perhaps be to use the LyX file lookup methods. But! I am
1203 going to implement this until I see some demand for it.
1207 // If the file can be found directly, we just return a
1208 // absolute path version of it.
1209 if (FileInfo(fil).exist())
1210 return MakeAbsPath(fil);
1212 // No we try to find it using kpsewhich.
1213 // It seems from the kpsewhich manual page that it is safe to use
1214 // kpsewhich without --format: "When the --format option is not
1215 // given, the search path used when looking for a file is inferred
1216 // from the name given, by looking for a known extension. If no
1217 // known extension is found, the search path for TeX source files
1219 // However, we want to take advantage of the format sine almost all
1220 // the different formats has environment variables that can be used
1221 // to controll which paths to search. f.ex. bib looks in
1222 // BIBINPUTS and TEXBIB. Small list follows:
1223 // bib - BIBINPUTS, TEXBIB
1225 // graphic/figure - TEXPICTS, TEXINPUTS
1226 // ist - TEXINDEXSTYLE, INDEXSTYLE
1227 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1229 // tfm - TFMFONTS, TEXFONTS
1230 // This means that to use kpsewhich in the best possible way we
1231 // should help it by setting additional path in the approp. envir.var.
1232 string const kpsecmd = "kpsewhich " + fil;
1234 cmd_ret const c = RunCommand(kpsecmd);
1236 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1237 << "kpse result = `" << rtrim(c.second, "\n")
1240 return os::internal_path(rtrim(c.second, "\n\r"));
1246 void removeAutosaveFile(string const & filename)
1248 string a = OnlyPath(filename);
1250 a += OnlyFilename(filename);
1252 FileInfo const fileinfo(a);
1253 if (fileinfo.exist())
1258 void readBB_lyxerrMessage(string const & file, bool & zipped,
1259 string const & message)
1261 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1262 << message << std::endl;
1263 #warning Why is this func deleting a file? (Lgb)
1269 string const readBB_from_PSFile(string const & file)
1271 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1272 // It seems that every command in the header has an own line,
1273 // getline() should work for all files.
1274 // On the other hand some plot programs write the bb at the
1275 // end of the file. Than we have in the header:
1276 // %%BoundingBox: (atend)
1277 // In this case we must check the end.
1278 bool zipped = zippedFile(file);
1279 string const file_ = zipped ?
1280 string(unzipFile(file)) : string(file);
1281 string const format = getExtFromContents(file_);
1283 if (format != "eps" && format != "ps") {
1284 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1288 std::ifstream is(file_.c_str());
1292 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1293 string const bb = ltrim(s.substr(14));
1294 readBB_lyxerrMessage(file_, zipped, bb);
1298 readBB_lyxerrMessage(file_, zipped, "no bb found");
1303 int compare_timestamps(string const & file1, string const & file2)
1305 BOOST_ASSERT(AbsolutePath(file1) && AbsolutePath(file2));
1307 // If the original is newer than the copy, then copy the original
1308 // to the new directory.
1313 if (f1.exist() && f2.exist()) {
1314 double const tmp = difftime(f1.getModificationTime(),
1315 f2.getModificationTime());
1317 cmp = tmp > 0 ? 1 : -1;
1319 } else if (f1.exist()) {
1321 } else if (f2.exist()) {
1328 } //namespace support