3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * parts Copyright 1985, 1990, 1993 Free Software Foundation, Inc.
8 * \author Ivan Schreter
9 * \author Dirk Niggemann
10 * \author Asger Alstrup
11 * \author Lars Gullik Bjønnes
12 * \author Jean-Marc Lasgouttes
14 * Full author contact details are available in file CREDITS
16 * General path-mangling functions
22 #include "support/lstrings.h"
23 #include "support/systemcall.h"
24 #include "support/LAssert.h"
26 #include "filetools.h"
29 #include "support/path.h" // I know it's OS/2 specific (SMiyata)
46 // Which part of this is still necessary? (JMarc).
49 # define NAMLEN(dirent) strlen((dirent)->d_name)
51 # define dirent direct
52 # define NAMLEN(dirent) (dirent)->d_namlen
54 # include <sys/ndir.h>
64 #ifndef CXX_GLOBAL_CSTD
77 extern string system_lyxdir;
78 extern string build_lyxdir;
79 extern string user_lyxdir;
82 bool IsLyXFilename(string const & filename)
84 return suffixIs(ascii_lowercase(filename), ".lyx");
88 bool IsSGMLFilename(string const & filename)
90 return suffixIs(ascii_lowercase(filename), ".sgml");
94 // Substitutes spaces with underscores in filename (and path)
95 string const MakeLatexName(string const & file)
97 string name = OnlyFilename(file);
98 string const path = OnlyPath(file);
100 for (string::size_type i = 0; i < name.length(); ++i) {
101 name[i] &= 0x7f; // set 8th bit to 0
104 // ok so we scan through the string twice, but who cares.
105 string const keep("abcdefghijklmnopqrstuvwxyz"
106 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
107 "@!\"'()*+,-./0123456789:;<=>?[]`|");
109 string::size_type pos = 0;
110 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
113 return AddName(path, name);
117 // Substitutes spaces with underscores in filename (and path)
118 string const QuoteName(string const & name)
120 return (os::shell() == os::UNIX) ?
126 // Is a file readable ?
127 bool IsFileReadable(string const & path)
130 return file.isOK() && file.isRegular() && file.readable();
134 // Is a file read_only?
135 // return 1 read-write
137 // -1 error (doesn't exist, no access, anything else)
138 int IsFileWriteable(string const & path)
142 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
144 if (fi.readable()) // read-only
146 return -1; // everything else.
150 //returns true: dir writeable
151 // false: not writeable
152 bool IsDirWriteable(string const & path)
154 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
156 string const tmpfl(lyx::tempName(path, "lyxwritetest"));
166 // Uses a string of paths separated by ";"s to find a file to open.
167 // Can't cope with pathnames with a ';' in them. Returns full path to file.
168 // If path entry begins with $$LyX/, use system_lyxdir
169 // If path entry begins with $$User/, use user_lyxdir
170 // Example: "$$User/doc;$$LyX/doc"
171 string const FileOpenSearch(string const & path, string const & name,
176 bool notfound = true;
177 string tmppath = split(path, path_element, ';');
179 while (notfound && !path_element.empty()) {
180 path_element = os::slashify_path(path_element);
181 if (!suffixIs(path_element, '/'))
183 path_element = subst(path_element, "$$LyX", system_lyxdir);
184 path_element = subst(path_element, "$$User", user_lyxdir);
186 real_file = FileSearch(path_element, name, ext);
188 if (real_file.empty()) {
190 tmppath = split(tmppath, path_element, ';');
191 } while (!tmppath.empty() && path_element.empty());
197 if (ext.empty() && notfound) {
198 real_file = FileOpenSearch(path, name, "exe");
199 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
206 /// Returns a vector of all files in directory dir having extension ext.
207 vector<string> const DirList(string const & dir, string const & ext)
209 // This is a non-error checking C/system implementation
211 if (!ext.empty() && ext[0] != '.')
215 vector<string> dirlist;
216 DIR * dirp = ::opendir(dir.c_str());
219 << "Directory \"" << dir
220 << "\" does not exist to DirList." << endl;
225 while ((dire = ::readdir(dirp))) {
226 string const fil = dire->d_name;
227 if (suffixIs(fil, extension)) {
228 dirlist.push_back(fil);
233 /* I would have prefered to take a vector<string>& as parameter so
234 that we could avoid the copy of the vector when returning.
236 dirlist.swap(argvec);
237 to avoid the copy. (Lgb)
239 /* A C++ implementaion will look like this:
240 string extension(ext);
241 if (extension[0] != '.') extension.insert(0, 1, '.');
242 vector<string> dirlist;
243 directory_iterator dit("dir");
244 while (dit != directory_iterator()) {
245 string fil = dit->filename;
246 if (prefixIs(fil, extension)) {
247 dirlist.push_back(fil);
251 dirlist.swap(argvec);
257 // Returns the real name of file name in directory path, with optional
259 string const FileSearch(string const & path, string const & name,
262 // if `name' is an absolute path, we ignore the setting of `path'
263 // Expand Environmentvariables in 'name'
264 string const tmpname = ReplaceEnvironmentPath(name);
265 string fullname = MakeAbsPath(tmpname, path);
266 // search first without extension, then with it.
267 if (IsFileReadable(fullname))
269 else if (ext.empty())
271 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
274 if (IsFileReadable(fullname))
282 // Search the file name.ext in the subdirectory dir of
284 // 2) build_lyxdir (if not empty)
286 string const LibFileSearch(string const & dir, string const & name,
289 string fullname = FileSearch(AddPath(user_lyxdir, dir), name, ext);
290 if (!fullname.empty())
293 if (!build_lyxdir.empty())
294 fullname = FileSearch(AddPath(build_lyxdir, dir), name, ext);
295 if (!fullname.empty())
298 return FileSearch(AddPath(system_lyxdir, dir), name, ext);
303 i18nLibFileSearch(string const & dir, string const & name,
306 // this comment is from intl/dcigettext.c. We try to mimick this
308 /* The highest priority value is the `LANGUAGE' environment
309 variable. But we don't use the value if the currently
310 selected locale is the C locale. This is a GNU extension. */
312 string const lc_all = GetEnv("LC_ALL");
313 string lang = GetEnv("LANGUAGE");
314 if (lang.empty() || lc_all == "C") {
317 lang = GetEnv("LANG");
321 lang = token(lang, '_', 0);
323 if (lang.empty() || lang == "C")
324 return LibFileSearch(dir, name, ext);
326 string const tmp = LibFileSearch(dir, lang + '_' + name,
331 return LibFileSearch(dir, name, ext);
336 string const LibScriptSearch(string const & command)
339 string args = command;
340 args = split(args, script, ' ');
341 script = LibFileSearch("scripts", script);
344 else if (args.empty())
347 return script + ' ' + args;
351 string const GetEnv(string const & envname)
353 // f.ex. what about error checking?
354 char const * const ch = getenv(envname.c_str());
355 string const envstr = !ch ? "" : ch;
360 string const GetEnvPath(string const & name)
363 string const pathlist = subst(GetEnv(name), ':', ';');
365 string const pathlist = os::slashify_path(GetEnv(name));
367 return rtrim(pathlist, ";");
373 int DeleteAllFilesInDir(string const & path)
375 // I have decided that we will be using parts from the boost
376 // library. Check out http://www.boost.org/
377 // For directory access we will then use the directory_iterator.
378 // Then the code will be something like:
379 // directory_iterator dit(path);
380 // directory_iterator dend;
381 // if (dit == dend) {
384 // for (; dit != dend; ++dit) {
385 // string filename(*dit);
386 // if (filename == "." || filename == "..")
388 // string unlinkpath(AddName(path, filename));
389 // lyx::unlink(unlinkpath);
392 DIR * dir = ::opendir(path.c_str());
397 int return_value = 0;
398 while ((de = readdir(dir))) {
399 string const temp = de->d_name;
400 if (temp == "." || temp == "..")
402 string const unlinkpath = AddName (path, temp);
404 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
408 FileInfo fi(unlinkpath);
409 if (fi.isOK() && fi.isDir())
410 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
411 deleted &= (lyx::unlink(unlinkpath) == 0);
420 string const CreateTmpDir(string const & tempdir, string const & mask)
423 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
424 << "CreateTmpDir: mask=`" << mask << '\'' << endl;
426 string const tmpfl(lyx::tempName(tempdir, mask));
427 // lyx::tempName actually creates a file to make sure that it
428 // stays unique. So we have to delete it before we can create
429 // a dir with the same name. Note also that we are not thread
430 // safe because of the gap between unlink and mkdir. (Lgb)
431 lyx::unlink(tmpfl.c_str());
433 if (tmpfl.empty() || lyx::mkdir(tmpfl, 0700))
436 return MakeAbsPath(tmpfl);
442 int destroyDir(string const & tmpdir)
447 if (DeleteAllFilesInDir(tmpdir))
450 if (lyx::rmdir(tmpdir))
457 string const CreateBufferTmpDir(string const & pathfor)
460 static string const tmpdir(pathfor.empty() ? os::getTmpDir() : pathfor);
461 // We are in our own directory. Why bother to mangle name?
462 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
464 string const tmpfl = tmpdir + "/lyx_tmpbuf" + tostr(count++);
465 if (lyx::mkdir(tmpfl, 0777)) {
472 string const CreateLyXTmpDir(string const & deflt)
474 if ((!deflt.empty()) && (deflt != "/tmp")) {
475 if (lyx::mkdir(deflt, 0777)) {
479 return CreateTmpDir(deflt, "lyx_tmpdir");
486 return CreateTmpDir("/tmp", "lyx_tmpdir");
491 bool createDirectory(string const & path, int permission)
493 string temp(rtrim(os::slashify_path(path), "/"));
495 lyx::Assert(!temp.empty());
497 if (lyx::mkdir(temp, permission))
504 // Strip filename from path name
505 string const OnlyPath(string const & Filename)
507 // If empty filename, return empty
508 if (Filename.empty()) return Filename;
510 // Find last / or start of filename
511 string::size_type j = Filename.rfind('/');
512 if (j == string::npos)
514 return Filename.substr(0, j + 1);
518 // Convert relative path into absolute path based on a basepath.
519 // If relpath is absolute, just use that.
520 // If basepath is empty, use CWD as base.
521 string const MakeAbsPath(string const & RelPath, string const & BasePath)
523 // checks for already absolute path
524 if (os::is_absolute_path(RelPath))
527 // Copies given paths
528 string TempRel(os::slashify_path(RelPath));
529 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
530 TempRel = subst(TempRel, "//", "/");
534 if (os::is_absolute_path(BasePath))
537 TempBase = AddPath(lyx::getcwd(), BasePath);
539 // Handle /./ at the end of the path
540 while (suffixIs(TempBase, "/./"))
541 TempBase.erase(TempBase.length() - 2);
543 // processes relative path
544 string RTemp(TempRel);
547 while (!RTemp.empty()) {
549 RTemp = split(RTemp, Temp, '/');
551 if (Temp == ".") continue;
553 // Remove one level of TempBase
554 string::difference_type i = TempBase.length() - 2;
557 while (i > 0 && TempBase[i] != '/') --i;
561 while (i > 2 && TempBase[i] != '/') --i;
564 TempBase.erase(i, string::npos);
567 } else if (Temp.empty() && !RTemp.empty()) {
568 TempBase = os::current_root() + RTemp;
571 // Add this piece to TempBase
572 if (!suffixIs(TempBase, '/'))
578 // returns absolute path
579 return os::slashify_path(TempBase);
583 // Correctly append filename to the pathname.
584 // If pathname is '.', then don't use pathname.
585 // Chops any path of filename.
586 string const AddName(string const & path, string const & fname)
589 string const basename(OnlyFilename(fname));
593 if (path != "." && path != "./" && !path.empty()) {
594 buf = os::slashify_path(path);
595 if (!suffixIs(path, '/'))
599 return buf + basename;
603 // Strips path from filename
604 string const OnlyFilename(string const & fname)
609 string::size_type j = fname.rfind('/');
610 if (j == string::npos) // no '/' in fname
614 return fname.substr(j + 1);
618 /// Returns true is path is absolute
619 bool AbsolutePath(string const & path)
621 return os::is_absolute_path(path);
626 // Create absolute path. If impossible, don't do anything
627 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
628 string const ExpandPath(string const & path)
630 // checks for already absolute path
631 string RTemp(ReplaceEnvironmentPath(path));
632 if (os::is_absolute_path(RTemp))
636 string const copy(RTemp);
639 RTemp = split(RTemp, Temp, '/');
642 return lyx::getcwd() /*GetCWD()*/ + '/' + RTemp;
645 return GetEnvPath("HOME") + '/' + RTemp;
648 return MakeAbsPath(copy);
650 // Don't know how to handle this
656 // Constracts path/../path
657 // Can't handle "../../" or "/../" (Asger)
658 string const NormalizePath(string const & path)
664 if (os::is_absolute_path(path))
667 // Make implicit current directory explicit
670 while (!RTemp.empty()) {
672 RTemp = split(RTemp, Temp, '/');
676 } else if (Temp == "..") {
677 // Remove one level of TempBase
678 string::difference_type i = TempBase.length() - 2;
679 while (i > 0 && TempBase[i] != '/')
681 if (i >= 0 && TempBase[i] == '/')
682 TempBase.erase(i + 1, string::npos);
686 TempBase += Temp + '/';
690 // returns absolute path
695 string const GetFileContents(string const & fname)
697 FileInfo finfo(fname);
699 ifstream ifs(fname.c_str());
704 return STRCONV(ofs.str());
707 lyxerr << "LyX was not able to read file '" << fname << '\'' << endl;
713 // Search ${...} as Variable-Name inside the string and replace it with
714 // the denoted environmentvariable
715 // Allow Variables according to
716 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
719 string const ReplaceEnvironmentPath(string const & path)
722 // CompareChar: Environment variables starts with this character
723 // PathChar: Next path component start with this character
724 // while CompareChar found do:
725 // Split String with PathChar
726 // Search Environmentvariable
727 // if found: Replace Strings
729 char const CompareChar = '$';
730 char const FirstChar = '{';
731 char const EndChar = '}';
732 char const UnderscoreChar = '_';
733 string EndString; EndString += EndChar;
734 string FirstString; FirstString += FirstChar;
735 string CompareString; CompareString += CompareChar;
736 string const RegExp("*}*"); // Exist EndChar inside a String?
738 // first: Search for a '$' - Sign.
740 string result1; //(copy); // for split-calls
741 string result0 = split(path, result1, CompareChar);
742 while (!result0.empty()) {
743 string copy1(result0); // contains String after $
745 // Check, if there is an EndChar inside original String.
747 if (!regexMatch(copy1, RegExp)) {
748 // No EndChar inside. So we are finished
749 result1 += CompareString + result0;
755 string res0 = split(copy1, res1, EndChar);
756 // Now res1 holds the environmentvariable
757 // First, check, if Contents is ok.
758 if (res1.empty()) { // No environmentvariable. Continue Loop.
759 result1 += CompareString + FirstString;
763 // check contents of res1
764 char const * res1_contents = res1.c_str();
765 if (*res1_contents != FirstChar) {
766 // Again No Environmentvariable
767 result1 += CompareString;
771 // Check for variable names
772 // Situation ${} is detected as "No Environmentvariable"
773 char const * cp1 = res1_contents + 1;
774 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
776 while (*cp1 && result) {
777 result = isalnum(*cp1) ||
778 (*cp1 == UnderscoreChar);
783 // no correct variable name
784 result1 += CompareString + res1 + EndString;
785 result0 = split(res0, res1, CompareChar);
790 string env(GetEnv(res1_contents + 1));
792 // Congratulations. Environmentvariable found
795 result1 += CompareString + res1 + EndString;
798 result0 = split(res0, res1, CompareChar);
805 // Make relative path out of two absolute paths
806 string const MakeRelPath(string const & abspath, string const & basepath)
807 // Makes relative path out of absolute path. If it is deeper than basepath,
808 // it's easy. If basepath and abspath share something (they are all deeper
809 // than some directory), it'll be rendered using ..'s. If they are completely
810 // different, then the absolute path will be used as relative path.
812 string::size_type const abslen = abspath.length();
813 string::size_type const baselen = basepath.length();
815 string::size_type i = os::common_path(abspath, basepath);
818 // actually no match - cannot make it relative
822 // Count how many dirs there are in basepath above match
823 // and append as many '..''s into relpath
825 string::size_type j = i;
826 while (j < baselen) {
827 if (basepath[j] == '/') {
828 if (j + 1 == baselen)
835 // Append relative stuff from common directory to abspath
836 if (abspath[i] == '/')
838 for (; i < abslen; ++i)
841 if (suffixIs(buf, '/'))
842 buf.erase(buf.length() - 1);
843 // Substitute empty with .
850 // Append sub-directory(ies) to a path in an intelligent way
851 string const AddPath(string const & path, string const & path_2)
854 string const path2 = os::slashify_path(path_2);
856 if (!path.empty() && path != "." && path != "./") {
857 buf = os::slashify_path(path);
858 if (path[path.length() - 1] != '/')
862 if (!path2.empty()) {
863 string::size_type const p2start = path2.find_first_not_of('/');
864 string::size_type const p2end = path2.find_last_not_of('/');
865 string const tmp = path2.substr(p2start, p2end - p2start + 1);
873 Change extension of oldname to extension.
874 Strips path off if no_path == true.
875 If no extension on oldname, just appends.
877 string const ChangeExtension(string const & oldname, string const & extension)
879 string::size_type const last_slash = oldname.rfind('/');
880 string::size_type last_dot = oldname.rfind('.');
881 if (last_dot < last_slash && last_slash != string::npos)
882 last_dot = string::npos;
885 // Make sure the extension starts with a dot
886 if (!extension.empty() && extension[0] != '.')
887 ext= '.' + extension;
891 return os::slashify_path(oldname.substr(0, last_dot) + ext);
895 /// Return the extension of the file (not including the .)
896 string const GetExtension(string const & name)
898 string::size_type const last_slash = name.rfind('/');
899 string::size_type const last_dot = name.rfind('.');
900 if (last_dot != string::npos &&
901 (last_slash == string::npos || last_dot > last_slash))
902 return name.substr(last_dot + 1,
903 name.length() - (last_dot + 1));
908 // the different filetypes and what they contain in one of the first lines
909 // (dots are any characters). (Herbert 20020131)
912 // EPS %!PS-Adobe-3.0 EPSF...
919 // PBM P1... or P4 (B/W)
920 // PGM P2... or P5 (Grayscale)
921 // PPM P3... or P6 (color)
922 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
923 // SGI \001\332... (decimal 474)
925 // TIFF II... or MM...
927 // XPM /* XPM */ sometimes missing (f.ex. tgif-export)
928 // ...static char *...
929 // XWD \000\000\000\151 (0x00006900) decimal 105
931 // GZIP \037\213\010\010... http://www.ietf.org/rfc/rfc1952.txt
932 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
933 // Z \037\177 UNIX compress
935 /// return the "extension" which belongs to the contents.
936 /// for no knowing contents return the extension. Without
937 /// an extension and unknown contents we return "user"
938 string const getExtFromContents(string const & filename)
941 if (filename.empty() || !IsFileReadable(filename))
945 ifstream ifs(filename.c_str());
947 // Couldn't open file...
951 string const gzipStamp = "\037\213\010\010";
954 string const zipStamp = "PK";
957 string const compressStamp = "\037\177";
959 // Maximum strings to read
960 int const max_count = 50;
964 bool firstLine = true;
965 while ((count++ < max_count) && format.empty()) {
967 lyxerr[Debug::GRAPHICS]
968 << "filetools(getExtFromContents)\n"
969 << "\tFile type not recognised before EOF!"
975 string const stamp = str.substr(0,2);
976 if (firstLine && str.size() >= 2) {
977 // at first we check for a zipped file, because this
978 // information is saved in the first bytes of the file!
979 // also some graphic formats which save the information
980 // in the first line, too.
981 if (prefixIs(str, gzipStamp)) {
984 } else if (stamp == zipStamp) {
987 } else if (stamp == compressStamp) {
991 } else if (stamp == "BM") {
994 } else if (stamp == "\001\332") {
998 // Don't need to use str.at(0), str.at(1) because
999 // we already know that str.size() >= 2
1000 } else if (str[0] == 'P') {
1016 } else if ((stamp == "II") || (stamp == "MM")) {
1019 } else if (prefixIs(str,"%TGIF")) {
1022 } else if (prefixIs(str,"#FIG")) {
1025 } else if (prefixIs(str,"GIF")) {
1028 } else if (str.size() > 3) {
1029 int const c = ((str[0] << 24) & (str[1] << 16) &
1030 (str[2] << 8) & str[3]);
1039 if (!format.empty())
1041 else if (contains(str,"EPSF"))
1042 // dummy, if we have wrong file description like
1043 // %!PS-Adobe-2.0EPSF"
1046 else if (contains(str,"Grace"))
1049 else if (contains(str,"JFIF"))
1052 else if (contains(str,"%PDF"))
1055 else if (contains(str,"PNG"))
1058 else if (contains(str,"%!PS-Adobe")) {
1061 if (contains(str,"EPSF"))
1067 else if (contains(str,"_bits[]"))
1070 else if (contains(str,"XPM") || contains(str, "static char *"))
1073 else if (contains(str,"BITPIX"))
1077 if (!format.empty()) {
1078 lyxerr[Debug::GRAPHICS]
1079 << "Recognised Fileformat: " << format << endl;
1083 string const ext(GetExtension(filename));
1084 lyxerr[Debug::GRAPHICS]
1085 << "filetools(getExtFromContents)\n"
1086 << "\tCouldn't find a known Type!\n";
1088 lyxerr[Debug::GRAPHICS]
1089 << "\twill take the file extension -> "
1093 lyxerr[Debug::GRAPHICS]
1094 << "\twill use ext or a \"user\" defined format" << endl;
1100 /// check for zipped file
1101 bool zippedFile(string const & name)
1103 string const type = getExtFromContents(name);
1104 if (contains("gzip zip compress", type) && !type.empty())
1110 string const unzipFile(string const & zipped_file)
1112 string const file = ChangeExtension(zipped_file, string());
1113 string const tempfile = lyx::tempName(string(), file);
1115 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1117 one.startscript(Systemcall::Wait, command);
1118 // test that command was executed successfully (anon)
1119 // yes, please do. (Lgb)
1124 // Creates a nice compact path for displaying
1126 MakeDisplayPath (string const & path, unsigned int threshold)
1128 string::size_type const l1 = path.length();
1130 // First, we try a relative path compared to home
1131 string const home(GetEnvPath("HOME"));
1132 string relhome = MakeRelPath(path, home);
1134 string::size_type l2 = relhome.length();
1138 // If we backup from home or don't have a relative path,
1139 // this try is no good
1140 if (prefixIs(relhome, "../") || os::is_absolute_path(relhome)) {
1141 // relative path was no good, just use the original path
1148 // Is the path too long?
1149 if (l2 > threshold) {
1155 while (relhome.length() > threshold)
1156 relhome = split(relhome, temp, '/');
1158 // Did we shortend everything away?
1159 if (relhome.empty()) {
1160 // Yes, filename in itself is too long.
1161 // Pick the start and the end of the filename.
1162 relhome = OnlyFilename(path);
1163 string const head = relhome.substr(0, threshold/2 - 3);
1165 l2 = relhome.length();
1167 relhome.substr(l2 - threshold/2 - 2, l2 - 1);
1168 relhome = head + "..." + tail;
1171 return prefix + relhome;
1175 bool LyXReadLink(string const & file, string & link, bool resolve)
1177 char linkbuffer[512];
1178 // Should be PATH_MAX but that needs autconf support
1179 int const nRead = ::readlink(file.c_str(),
1180 linkbuffer, sizeof(linkbuffer) - 1);
1183 linkbuffer[nRead] = '\0'; // terminator
1185 link = MakeAbsPath(linkbuffer, OnlyPath(file));
1192 cmd_ret const RunCommand(string const & cmd)
1194 // One question is if we should use popen or
1195 // create our own popen based on fork, exec, pipe
1196 // of course the best would be to have a
1197 // pstream (process stream), with the
1198 // variants ipstream, opstream
1200 FILE * inf = ::popen(cmd.c_str(), os::popen_read_mode());
1202 // (Claus Hentschel) Check if popen was succesful ;-)
1204 return make_pair(-1, string());
1209 ret += static_cast<char>(c);
1212 int const pret = pclose(inf);
1213 return make_pair(pret, ret);
1217 string const findtexfile(string const & fil, string const & /*format*/)
1219 /* There is no problem to extend this function too use other
1220 methods to look for files. It could be setup to look
1221 in environment paths and also if wanted as a last resort
1222 to a recursive find. One of the easier extensions would
1223 perhaps be to use the LyX file lookup methods. But! I am
1224 going to implement this until I see some demand for it.
1228 // If the file can be found directly, we just return a
1229 // absolute path version of it.
1230 if (FileInfo(fil).exist())
1231 return MakeAbsPath(fil);
1233 // No we try to find it using kpsewhich.
1234 // It seems from the kpsewhich manual page that it is safe to use
1235 // kpsewhich without --format: "When the --format option is not
1236 // given, the search path used when looking for a file is inferred
1237 // from the name given, by looking for a known extension. If no
1238 // known extension is found, the search path for TeX source files
1240 // However, we want to take advantage of the format sine almost all
1241 // the different formats has environment variables that can be used
1242 // to controll which paths to search. f.ex. bib looks in
1243 // BIBINPUTS and TEXBIB. Small list follows:
1244 // bib - BIBINPUTS, TEXBIB
1246 // graphic/figure - TEXPICTS, TEXINPUTS
1247 // ist - TEXINDEXSTYLE, INDEXSTYLE
1248 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1250 // tfm - TFMFONTS, TEXFONTS
1251 // This means that to use kpsewhich in the best possible way we
1252 // should help it by setting additional path in the approp. envir.var.
1253 string const kpsecmd = "kpsewhich " + fil;
1255 cmd_ret const c = RunCommand(kpsecmd);
1257 lyxerr[Debug::LATEX] << "kpse status = " << c.first << '\n'
1258 << "kpse result = `" << rtrim(c.second, "\n")
1261 return os::internal_path(rtrim(c.second, "\n\r"));
1267 void removeAutosaveFile(string const & filename)
1269 string a = OnlyPath(filename);
1271 a += OnlyFilename(filename);
1273 FileInfo const fileinfo(a);
1274 if (fileinfo.exist())
1279 void readBB_lyxerrMessage(string const & file, bool & zipped,
1280 string const & message)
1282 lyxerr[Debug::GRAPHICS] << "[readBB_from_PSFile] "
1283 << message << std::endl;
1289 string const readBB_from_PSFile(string const & file)
1291 // in a (e)ps-file it's an entry like %%BoundingBox:23 45 321 345
1292 // It seems that every command in the header has an own line,
1293 // getline() should work for all files.
1294 // On the other hand some plot programs write the bb at the
1295 // end of the file. Than we have in the header:
1296 // %%BoundingBox: (atend)
1297 // In this case we must check the end.
1298 bool zipped = zippedFile(file);
1299 string const file_ = zipped ?
1300 string(unzipFile(file)) : string(file);
1301 string const format = getExtFromContents(file_);
1303 if (format != "eps" && format != "ps") {
1304 readBB_lyxerrMessage(file_, zipped,"no(e)ps-format");
1308 std::ifstream is(file_.c_str());
1312 if (contains(s,"%%BoundingBox:") && !contains(s,"atend")) {
1313 string const bb = ltrim(s.substr(14));
1314 readBB_lyxerrMessage(file_, zipped, bb);
1318 readBB_lyxerrMessage(file_, zipped, "no bb found");