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
24 #include "support/convert.h"
25 #include "support/systemcall.h"
26 #include "support/filetools.h"
27 #include "support/lstrings.h"
28 #include "support/FileInfo.h"
29 #include "support/forkedcontr.h"
30 #include "support/package.h"
31 #include "support/path.h"
32 #include "support/lyxlib.h"
33 #include "support/os.h"
35 // FIXME Interface violation
39 #include <boost/assert.hpp>
40 #include <boost/regex.hpp>
41 #include <boost/tokenizer.hpp>
55 // Which part of this is still necessary? (JMarc).
58 # define NAMLEN(dirent) strlen((dirent)->d_name)
60 # define dirent direct
61 # define NAMLEN(dirent) (dirent)->d_namlen
63 # include <sys/ndir.h>
73 #ifndef CXX_GLOBAL_CSTD
84 using std::ostringstream;
91 bool IsLyXFilename(string const & filename)
93 return suffixIs(ascii_lowercase(filename), ".lyx");
97 bool IsSGMLFilename(string const & filename)
99 return suffixIs(ascii_lowercase(filename), ".sgml");
103 // Substitutes spaces with underscores in filename (and path)
104 string const MakeLatexName(string const & file)
106 string name = OnlyFilename(file);
107 string const path = OnlyPath(file);
109 for (string::size_type i = 0; i < name.length(); ++i) {
110 name[i] &= 0x7f; // set 8th bit to 0
113 // ok so we scan through the string twice, but who cares.
114 string const keep("abcdefghijklmnopqrstuvwxyz"
115 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
116 "@!\"'()*+,-./0123456789:;<=>?[]`|");
118 string::size_type pos = 0;
119 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
122 return AddName(path, name);
126 // Substitutes spaces with underscores in filename (and path)
127 string const QuoteName(string const & name)
129 return (os::shell() == os::UNIX) ?
135 // Is a file readable ?
136 bool IsFileReadable(string const & path)
139 return file.isOK() && file.isRegular() && file.readable();
143 // Is a file read_only?
144 // return 1 read-write
146 // -1 error (doesn't exist, no access, anything else)
147 int IsFileWriteable(string const & path)
151 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
153 if (fi.readable()) // read-only
155 return -1; // everything else.
159 //returns true: dir writeable
160 // false: not writeable
161 bool IsDirWriteable(string const & path)
163 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
165 string const tmpfl(tempName(path, "lyxwritetest"));
175 // Uses a string of paths separated by ";"s to find a file to open.
176 // Can't cope with pathnames with a ';' in them. Returns full path to file.
177 // If path entry begins with $$LyX/, use system_lyxdir
178 // If path entry begins with $$User/, use user_lyxdir
179 // Example: "$$User/doc;$$LyX/doc"
180 string const FileOpenSearch(string const & path, string const & name,
185 bool notfound = true;
186 string tmppath = split(path, path_element, ';');
188 while (notfound && !path_element.empty()) {
189 path_element = os::internal_path(path_element);
190 if (!suffixIs(path_element, '/'))
192 path_element = subst(path_element, "$$LyX",
193 package().system_support());
194 path_element = subst(path_element, "$$User",
195 package().user_support());
197 real_file = FileSearch(path_element, name, ext);
199 if (real_file.empty()) {
201 tmppath = split(tmppath, path_element, ';');
202 } while (!tmppath.empty() && path_element.empty());
208 if (ext.empty() && notfound) {
209 real_file = FileOpenSearch(path, name, "exe");
210 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
217 /// Returns a vector of all files in directory dir having extension ext.
218 vector<string> const DirList(string const & dir, string const & ext)
220 // This is a non-error checking C/system implementation
222 if (!ext.empty() && ext[0] != '.')
226 vector<string> dirlist;
227 DIR * dirp = ::opendir(dir.c_str());
230 << "Directory \"" << dir
231 << "\" does not exist to DirList." << endl;
236 while ((dire = ::readdir(dirp))) {
237 string const fil = dire->d_name;
238 if (suffixIs(fil, extension)) {
239 dirlist.push_back(fil);
244 /* I would have prefered to take a vector<string>& as parameter so
245 that we could avoid the copy of the vector when returning.
247 dirlist.swap(argvec);
248 to avoid the copy. (Lgb)
250 /* A C++ implementaion will look like this:
251 string extension(ext);
252 if (extension[0] != '.') extension.insert(0, 1, '.');
253 vector<string> dirlist;
254 directory_iterator dit("dir");
255 while (dit != directory_iterator()) {
256 string fil = dit->filename;
257 if (prefixIs(fil, extension)) {
258 dirlist.push_back(fil);
262 dirlist.swap(argvec);
268 // Returns the real name of file name in directory path, with optional
270 string const FileSearch(string const & path, string const & name,
273 // if `name' is an absolute path, we ignore the setting of `path'
274 // Expand Environmentvariables in 'name'
275 string const tmpname = ReplaceEnvironmentPath(name);
276 string fullname = MakeAbsPath(tmpname, path);
277 // search first without extension, then with it.
278 if (IsFileReadable(fullname))
280 else if (ext.empty())
282 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
285 if (IsFileReadable(fullname))
293 // Search the file name.ext in the subdirectory dir of
295 // 2) build_lyxdir (if not empty)
297 string const LibFileSearch(string const & dir, string const & name,
300 string fullname = FileSearch(AddPath(package().user_support(), dir),
302 if (!fullname.empty())
305 if (!package().build_support().empty())
306 fullname = FileSearch(AddPath(package().build_support(), dir),
308 if (!fullname.empty())
311 return FileSearch(AddPath(package().system_support(), dir), name, ext);
316 i18nLibFileSearch(string const & dir, string const & name,
319 // the following comments are from intl/dcigettext.c. We try
320 // to mimick this behaviour here.
321 /* The highest priority value is the `LANGUAGE' environment
322 variable. But we don't use the value if the currently
323 selected locale is the C locale. This is a GNU extension. */
324 /* [Otherwise] We have to proceed with the POSIX methods of
325 looking to `LC_ALL', `LC_xxx', and `LANG'. */
327 string lang = GetEnv("LC_ALL");
329 lang = GetEnv("LC_MESSAGES");
331 lang = GetEnv("LANG");
337 string const language = GetEnv("LANGUAGE");
338 if (lang != "C" && lang != "POSIX" && !language.empty())
342 lang = split(lang, l, ':');
343 while (!l.empty() && l != "C" && l != "POSIX") {
344 string const tmp = LibFileSearch(dir,
345 token(l, '_', 0) + '_' + name,
349 lang = split(lang, l, ':');
352 return LibFileSearch(dir, name, ext);
356 string const LibScriptSearch(string const & command_in)
358 string const token_scriptpath("$$s/");
360 string command = command_in;
361 // Find the starting position of "$$s/"
362 string::size_type const pos1 = command.find(token_scriptpath);
363 if (pos1 == string::npos)
365 // Find the end of the "$$s/some_script" word within command
366 string::size_type const start_script = pos1 + 4;
367 string::size_type const pos2 = command.find(' ', start_script);
368 string::size_type const size_script = pos2 == string::npos?
369 (command.size() - start_script) : pos2 - start_script;
371 // Does this script file exist?
372 string const script =
373 LibFileSearch("scripts", command.substr(start_script, size_script));
375 if (script.empty()) {
376 // Replace "$$s/" with ""
377 command.erase(pos1, 4);
379 // Replace "$$s/some_script" with "$LYX_SCRIPT_PATH/some_script"
380 string::size_type const size_replace = size_script + 4;
381 command.replace(pos1, size_replace, QuoteName(script));
388 string const GetEnv(string const & envname)
390 // f.ex. what about error checking?
391 char const * const ch = getenv(envname.c_str());
392 string const envstr = !ch ? "" : ch;
397 vector<string> const getEnvPath(string const & name)
399 typedef boost::char_separator<char> Separator;
400 typedef boost::tokenizer<Separator> Tokenizer;
402 #if defined (__EMX__) || defined (_WIN32)
403 Separator const separator(";");
405 Separator const separator(":");
408 string const env_var = GetEnv(name);
409 Tokenizer const tokens(env_var, separator);
410 Tokenizer::const_iterator it = tokens.begin();
411 Tokenizer::const_iterator const end = tokens.end();
413 std::vector<string> vars;
414 for (; it != end; ++it)
415 vars.push_back(os::internal_path(*it));
421 string const GetEnvPath(string const & name)
424 string const pathlist = subst(GetEnv(name), ':', ';');
426 string const pathlist = os::internal_path(GetEnv(name));
428 return rtrim(pathlist, ";");
434 int DeleteAllFilesInDir(string const & path)
436 // I have decided that we will be using parts from the boost
437 // library. Check out http://www.boost.org/
438 // For directory access we will then use the directory_iterator.
439 // Then the code will be something like:
440 // directory_iterator dit(path);
441 // directory_iterator dend;
442 // if (dit == dend) {
445 // for (; dit != dend; ++dit) {
446 // string filename(*dit);
447 // if (filename == "." || filename == "..")
449 // string unlinkpath(AddName(path, filename));
450 // lyx::unlink(unlinkpath);
453 DIR * dir = ::opendir(path.c_str());
458 int return_value = 0;
459 while ((de = readdir(dir))) {
460 string const temp = de->d_name;
461 if (temp == "." || temp == "..")
463 string const unlinkpath = AddName (path, temp);
465 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
469 FileInfo fi(unlinkpath);
470 if (fi.isOK() && fi.isDir()) {
471 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
472 deleted &= (rmdir(unlinkpath) == 0);
474 deleted &= (unlink(unlinkpath) == 0);
483 string const createTmpDir(string const & tempdir, string const & mask)
486 << "createTmpDir: tempdir=`" << tempdir << "'\n"
487 << "createTmpDir: mask=`" << mask << '\'' << endl;
489 string const tmpfl(tempName(tempdir, mask));
490 // lyx::tempName actually creates a file to make sure that it
491 // stays unique. So we have to delete it before we can create
492 // a dir with the same name. Note also that we are not thread
493 // safe because of the gap between unlink and mkdir. (Lgb)
496 if (tmpfl.empty() || mkdir(tmpfl, 0700)) {
497 lyxerr << "LyX could not create the temporary directory '"
498 << tmpfl << "'" << endl;
502 return MakeAbsPath(tmpfl);
508 int destroyDir(string const & tmpdir)
511 Path p(user_lyxdir());
513 if (DeleteAllFilesInDir(tmpdir))
523 string const createBufferTmpDir()
526 // We are in our own directory. Why bother to mangle name?
527 // In fact I wrote this code to circumvent a problematic behaviour
528 // (bug?) of EMX mkstemp().
530 package().temp_dir() + "/lyx_tmpbuf" +
531 convert<string>(count++);
533 if (mkdir(tmpfl, 0777)) {
534 lyxerr << "LyX could not create the temporary directory '"
535 << tmpfl << "'" << endl;
542 string const createLyXTmpDir(string const & deflt)
544 if (!deflt.empty() && deflt != "/tmp") {
545 if (mkdir(deflt, 0777)) {
546 if (IsDirWriteable(deflt))
547 // deflt could not be created because it
548 // did exist already, so let's create our own
551 Path p(user_lyxdir());
553 return createTmpDir(deflt, "lyx_tmpdir");
555 // some other error occured.
557 Path p(user_lyxdir());
559 return createTmpDir("/tmp", "lyx_tmpdir");
564 Path p(user_lyxdir());
566 return createTmpDir("/tmp", "lyx_tmpdir");
571 bool createDirectory(string const & path, int permission)
573 string temp(rtrim(os::internal_path(path), "/"));
575 BOOST_ASSERT(!temp.empty());
577 if (mkdir(temp, permission))
584 // Strip filename from path name
585 string const OnlyPath(string const & Filename)
587 // If empty filename, return empty
588 if (Filename.empty()) return Filename;
590 // Find last / or start of filename
591 string::size_type j = Filename.rfind('/');
592 if (j == string::npos)
594 return Filename.substr(0, j + 1);
598 // Convert relative path into absolute path based on a basepath.
599 // If relpath is absolute, just use that.
600 // If basepath is empty, use CWD as base.
601 string const MakeAbsPath(string const & RelPath, string const & BasePath)
603 // checks for already absolute path
604 if (os::is_absolute_path(RelPath))
607 // Copies given paths
608 string TempRel(os::internal_path(RelPath));
609 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
610 TempRel = subst(TempRel, "//", "/");
614 if (os::is_absolute_path(BasePath))
617 TempBase = AddPath(getcwd(), BasePath);
619 // Handle /./ at the end of the path
620 while (suffixIs(TempBase, "/./"))
621 TempBase.erase(TempBase.length() - 2);
623 // processes relative path
624 string RTemp(TempRel);
627 while (!RTemp.empty()) {
629 RTemp = split(RTemp, Temp, '/');
631 if (Temp == ".") continue;
633 // Remove one level of TempBase
634 string::difference_type i = TempBase.length() - 2;
637 while (i > 0 && TempBase[i] != '/') --i;
641 while (i > 2 && TempBase[i] != '/') --i;
644 TempBase.erase(i, string::npos);
647 } else if (Temp.empty() && !RTemp.empty()) {
648 TempBase = os::current_root() + RTemp;
651 // Add this piece to TempBase
652 if (!suffixIs(TempBase, '/'))
658 // returns absolute path
659 return os::internal_path(TempBase);
663 // Correctly append filename to the pathname.
664 // If pathname is '.', then don't use pathname.
665 // Chops any path of filename.
666 string const AddName(string const & path, string const & fname)
669 string const basename(OnlyFilename(fname));
673 if (path != "." && path != "./" && !path.empty()) {
674 buf = os::internal_path(path);
675 if (!suffixIs(path, '/'))
679 return buf + basename;
683 // Strips path from filename
684 string const OnlyFilename(string const & fname)
689 string::size_type j = fname.rfind('/');
690 if (j == string::npos) // no '/' in fname
694 return fname.substr(j + 1);
698 /// Returns true is path is absolute
699 bool AbsolutePath(string const & path)
701 return os::is_absolute_path(path);
706 // Create absolute path. If impossible, don't do anything
707 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
708 string const ExpandPath(string const & path)
710 // checks for already absolute path
711 string RTemp(ReplaceEnvironmentPath(path));
712 if (os::is_absolute_path(RTemp))
716 string const copy(RTemp);
719 RTemp = split(RTemp, Temp, '/');
722 return getcwd() + '/' + RTemp;
725 return package().home_dir() + '/' + RTemp;
728 return MakeAbsPath(copy);
730 // Don't know how to handle this
736 // Constracts path/../path
737 // Can't handle "../../" or "/../" (Asger)
738 // Also converts paths like /foo//bar ==> /foo/bar
739 string const NormalizePath(string const & path)
745 if (os::is_absolute_path(path))
748 // Make implicit current directory explicit
751 // Normalise paths like /foo//bar ==> /foo/bar
752 boost::RegEx regex("/{2,}");
753 RTemp = regex.Merge(RTemp, "/");
755 while (!RTemp.empty()) {
757 RTemp = split(RTemp, Temp, '/');
761 } else if (Temp == "..") {
762 // Remove one level of TempBase
763 string::difference_type i = TempBase.length() - 2;
764 while (i > 0 && TempBase[i] != '/')
766 if (i >= 0 && TempBase[i] == '/')
767 TempBase.erase(i + 1, string::npos);
771 TempBase += Temp + '/';
775 // returns absolute path
780 string const GetFileContents(string const & fname)
782 FileInfo finfo(fname);
784 ifstream ifs(fname.c_str());
792 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
797 // Search the string for ${VAR} and $VAR and replace VAR using getenv.
798 string const ReplaceEnvironmentPath(string const & path)
800 // ${VAR} is defined as
801 // $\{[A-Za-z_][A-Za-z_0-9]*\}
802 static string const envvar_br = "[$]\\{([A-Za-z_][A-Za-z_0-9]*)\\}";
804 // $VAR is defined as:
805 // $\{[A-Za-z_][A-Za-z_0-9]*\}
806 static string const envvar = "[$]([A-Za-z_][A-Za-z_0-9]*)";
808 static boost::regex envvar_br_re("(.*)" + envvar_br + "(.*)");
809 static boost::regex envvar_re("(.*)" + envvar + "(.*)");
812 string result = path;
814 regex_match(result, what, envvar_br_re);
815 if (!what[0].matched) {
816 regex_match(result, what, envvar_re);
817 if (!what[0].matched)
820 result = what.str(1) + GetEnv(what.str(2)) + what.str(3);
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::internal_path(path_2);
877 if (!path.empty() && path != "." && path != "./") {
878 buf = os::internal_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::internal_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));
930 // the different filetypes and what they contain in one of the first lines
931 // (dots are any characters). (Herbert 20020131)
934 // EPS %!PS-Adobe-3.0 EPSF...
941 // PBM P1... or P4 (B/W)
942 // PGM P2... or P5 (Grayscale)
943 // PPM P3... or P6 (color)
944 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
945 // SGI \001\332... (decimal 474)
947 // TIFF II... or MM...
949 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
950 // ...static char *...
951 // XWD \000\000\000\151 (0x00006900) decimal 105
953 // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt
954 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
955 // Z \037\235 UNIX compress
957 string const getFormatFromContents(string const & filename)
960 if (filename.empty() || !IsFileReadable(filename))
963 ifstream ifs(filename.c_str());
965 // Couldn't open file...
969 string const gzipStamp = "\037\213";
972 string const zipStamp = "PK";
975 string const compressStamp = "\037\235";
977 // Maximum strings to read
978 int const max_count = 50;
983 bool firstLine = true;
984 while ((count++ < max_count) && format.empty()) {
986 lyxerr[Debug::GRAPHICS]
987 << "filetools(getFormatFromContents)\n"
988 << "\tFile type not recognised before EOF!"
994 string const stamp = str.substr(0,2);
995 if (firstLine && str.size() >= 2) {
996 // at first we check for a zipped file, because this
997 // information is saved in the first bytes of the file!
998 // also some graphic formats which save the information
999 // in the first line, too.
1000 if (prefixIs(str, gzipStamp)) {
1003 } else if (stamp == zipStamp) {
1006 } else if (stamp == compressStamp) {
1007 format = "compress";
1009 // the graphics part
1010 } else if (stamp == "BM") {
1013 } else if (stamp == "\001\332") {
1017 // Don't need to use str.at(0), str.at(1) because
1018 // we already know that str.size() >= 2
1019 } else if (str[0] == 'P') {
1035 } else if ((stamp == "II") || (stamp == "MM")) {
1038 } else if (prefixIs(str,"%TGIF")) {
1041 } else if (prefixIs(str,"#FIG")) {
1044 } else if (prefixIs(str,"GIF")) {
1047 } else if (str.size() > 3) {
1048 int const c = ((str[0] << 24) & (str[1] << 16) &
1049 (str[2] << 8) & str[3]);
1058 if (!format.empty())
1060 else if (contains(str,"EPSF"))
1061 // dummy, if we have wrong file description like
1062 // %!PS-Adobe-2.0EPSF"
1065 else if (contains(str,"Grace"))
1068 else if (contains(str,"JFIF"))
1071 else if (contains(str,"%PDF"))
1074 else if (contains(str,"PNG"))
1077 else if (contains(str,"%!PS-Adobe")) {
1080 if (contains(str,"EPSF"))
1086 else if (contains(str,"_bits[]"))
1089 else if (contains(str,"XPM") || contains(str, "static char *"))
1092 else if (contains(str,"BITPIX"))
1096 if (!format.empty()) {
1097 lyxerr[Debug::GRAPHICS]
1098 << "Recognised Fileformat: " << format << endl;
1102 lyxerr[Debug::GRAPHICS]
1103 << "filetools(getFormatFromContents)\n"
1104 << "\tCouldn't find a known format!\n";
1109 /// check for zipped file
1110 bool zippedFile(string const & name)
1112 string const type = getFormatFromContents(name);
1113 if (contains("gzip zip compress", type) && !type.empty())
1119 string const unzippedFileName(string const & zipped_file)
1121 string const ext = GetExtension(zipped_file);
1122 if (ext == "gz" || ext == "z" || ext == "Z")
1123 return ChangeExtension(zipped_file, string());
1124 return "unzipped_" + zipped_file;
1128 string const unzipFile(string const & zipped_file, string const & unzipped_file)
1130 string const tempfile = unzipped_file.empty() ?
1131 unzippedFileName(zipped_file) : unzipped_file;
1133 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1135 one.startscript(Systemcall::Wait, command);
1136 // test that command was executed successfully (anon)
1137 // yes, please do. (Lgb)
1142 string const MakeDisplayPath(string const & path, unsigned int threshold)
1146 string const home(package().home_dir());
1148 // replace /home/blah with ~/
1149 if (prefixIs(str, home))
1150 str = subst(str, home, "~");
1152 if (str.length() <= threshold)
1155 string const prefix = ".../";
1158 while (str.length() > threshold)
1159 str = split(str, temp, '/');
1161 // Did we shorten everything away?
1163 // Yes, filename itself is too long.
1164 // Pick the start and the end of the filename.
1165 str = OnlyFilename(path);
1166 string const head = str.substr(0, threshold / 2 - 3);
1168 string::size_type len = str.length();
1170 str.substr(len - threshold / 2 - 2, len - 1);
1171 str = head + "..." + tail;
1174 return prefix + str;
1178 bool LyXReadLink(string const & file, string & link, bool resolve)
1180 #ifdef HAVE_READLINK
1181 char linkbuffer[512];
1182 // Should be PATH_MAX but that needs autconf support
1183 int const nRead = ::readlink(file.c_str(),
1184 linkbuffer, sizeof(linkbuffer) - 1);
1187 linkbuffer[nRead] = '\0'; // terminator
1189 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1199 cmd_ret const RunCommand(string const & cmd)
1201 // FIXME: replace all calls to RunCommand with ForkedCall
1202 // (if the output is not needed) or the code in ispell.C
1203 // (if the output is needed).
1205 // One question is if we should use popen or
1206 // create our own popen based on fork, exec, pipe
1207 // of course the best would be to have a
1208 // pstream (process stream), with the
1209 // variants ipstream, opstream
1211 sigset_t newMask, oldMask;
1212 sigemptyset(&oldMask);
1213 sigemptyset(&newMask);
1214 sigaddset(&newMask, SIGCHLD);
1216 // Block the SIGCHLD signal.
1217 sigprocmask(SIG_BLOCK, &newMask, &oldMask);
1219 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1221 // (Claus Hentschel) Check if popen was succesful ;-)
1223 return make_pair(-1, string());
1224 lyxerr << "RunCommand:: could not start child process" << endl;
1230 ret += static_cast<char>(c);
1233 int const pret = pclose(inf);
1235 perror("RunCommand:: could not terminate child process");
1237 // Unblock the SIGCHLD signal and restore the old mask.
1238 sigprocmask(SIG_SETMASK, &oldMask, 0);
1240 return make_pair(pret, ret);
1244 string const findtexfile(string const & fil, string const & /*format*/)
1246 /* There is no problem to extend this function too use other
1247 methods to look for files. It could be setup to look
1248 in environment paths and also if wanted as a last resort
1249 to a recursive find. One of the easier extensions would
1250 perhaps be to use the LyX file lookup methods. But! I am
1251 going to implement this until I see some demand for it.
1255 // If the file can be found directly, we just return a
1256 // absolute path version of it.
1257 if (FileInfo(fil).exist())
1258 return MakeAbsPath(fil);
1260 // No we try to find it using kpsewhich.
1261 // It seems from the kpsewhich manual page that it is safe to use
1262 // kpsewhich without --format: "When the --format option is not
1263 // given, the search path used when looking for a file is inferred
1264 // from the name given, by looking for a known extension. If no
1265 // known extension is found, the search path for TeX source files
1267 // However, we want to take advantage of the format sine almost all
1268 // the different formats has environment variables that can be used
1269 // to controll which paths to search. f.ex. bib looks in
1270 // BIBINPUTS and TEXBIB. Small list follows:
1271 // bib - BIBINPUTS, TEXBIB
1273 // graphic/figure - TEXPICTS, TEXINPUTS
1274 // ist - TEXINDEXSTYLE, INDEXSTYLE
1275 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1277 // tfm - TFMFONTS, TEXFONTS
1278 // This means that to use kpsewhich in the best possible way we
1279 // should help it by setting additional path in the approp. envir.var.
1280 string const kpsecmd = "kpsewhich " + fil;
1282 cmd_ret const c = RunCommand(kpsecmd);
1284 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1285 << "kpse result = `" << rtrim(c.second, "\n")
1288 return os::internal_path(rtrim(c.second, "\n\r"));
1294 void removeAutosaveFile(string const & filename)
1296 string a = OnlyPath(filename);
1298 a += OnlyFilename(filename);
1300 FileInfo const fileinfo(a);
1301 if (fileinfo.exist())
1306 void readBB_lyxerrMessage(string const & file, bool & zipped,
1307 string const & message)
1309 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1310 << message << std::endl;
1311 #ifdef WITH_WARNINGS
1312 #warning Why is this func deleting a file? (Lgb)
1319 string const readBB_from_PSFile(string const & file)
1321 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1322 // It seems that every command in the header has an own line,
1323 // getline() should work for all files.
1324 // On the other hand some plot programs write the bb at the
1325 // end of the file. Than we have in the header:
1326 // %%BoundingBox: (atend)
1327 // In this case we must check the end.
1328 bool zipped = zippedFile(file);
1329 string const file_ = zipped ?
1330 string(unzipFile(file)) : string(file);
1331 string const format = getFormatFromContents(file_);
1333 if (format != "eps" && format != "ps") {
1334 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1338 std::ifstream is(file_.c_str());
1342 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1343 string const bb = ltrim(s.substr(14));
1344 readBB_lyxerrMessage(file_, zipped, bb);
1348 readBB_lyxerrMessage(file_, zipped, "no bb found");
1353 int compare_timestamps(string const & file1, string const & file2)
1355 BOOST_ASSERT(AbsolutePath(file1) && AbsolutePath(file2));
1357 // If the original is newer than the copy, then copy the original
1358 // to the new directory.
1363 if (f1.exist() && f2.exist()) {
1364 double const tmp = difftime(f1.getModificationTime(),
1365 f2.getModificationTime());
1367 cmp = tmp > 0 ? 1 : -1;
1369 } else if (f1.exist()) {
1371 } else if (f2.exist()) {
1378 } //namespace support