2 filetools.C (former paths.C) - part of LyX project
3 General path-mangling functions
4 Copyright 1996 Ivan Schreter
5 Parts Copyright 1996 Dirk Niggemann
6 Parts Copyright 1985, 1990, 1993 Free Software Foundation, Inc.
7 Parts Copyright 1996 Asger Alstrup
11 lyx-filetool.C : tools functions for file/path handling
12 this file is part of LyX, the High Level Word Processor
13 Copyright 1995-1996, Matthias Ettrich and the LyX Team
27 #pragma implementation "filetools.h"
35 #include "support/lstrings.h"
37 #include "filetools.h"
38 #include "LSubstring.h"
39 #include "frontends/Alert.h"
41 #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>
70 extern string system_lyxdir;
71 extern string build_lyxdir;
72 extern string user_lyxdir;
73 extern string system_tempdir;
74 extern string system_packageList;
77 bool IsLyXFilename(string const & filename)
79 return suffixIs(lowercase(filename), ".lyx");
83 bool IsSGMLFilename(string const & filename)
85 return suffixIs(lowercase(filename), ".sgml");
89 // Substitutes spaces with underscores in filename (and path)
90 string const MakeLatexName(string const & file)
92 string name = OnlyFilename(file);
93 string const path = OnlyPath(file);
95 for (string::size_type i = 0; i < name.length(); ++i) {
96 name[i] &= 0x7f; // set 8th bit to 0
99 // ok so we scan through the string twice, but who cares.
100 string const keep("abcdefghijklmnopqrstuvwxyz"
101 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
102 "@!\"'()*+,-./0123456789:;<=>?[]`|");
104 string::size_type pos = 0;
105 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
108 return AddName(path, name);
112 // Substitutes spaces with underscores in filename (and path)
113 string const QuoteName(string const & name)
115 return (os::shell() == os::UNIX) ?
121 // Is a file readable ?
122 bool IsFileReadable (string const & path)
125 if (file.isOK() && file.isRegular() && file.readable())
132 // Is a file read_only?
133 // return 1 read-write
135 // -1 error (doesn't exist, no access, anything else)
136 int IsFileWriteable (string const & path)
140 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
142 if (fi.readable()) // read-only
144 return -1; // everything else.
148 //returns true: dir writeable
149 // false: not writeable
150 bool IsDirWriteable (string const & path)
152 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
154 string const tmpfl(lyx::tempName(path, "lyxwritetest"));
164 // Uses a string of paths separated by ";"s to find a file to open.
165 // Can't cope with pathnames with a ';' in them. Returns full path to file.
166 // If path entry begins with $$LyX/, use system_lyxdir
167 // If path entry begins with $$User/, use user_lyxdir
168 // Example: "$$User/doc;$$LyX/doc"
169 string const FileOpenSearch (string const & path, string const & name,
172 string real_file, path_element;
173 bool notfound = true;
174 string tmppath = split(path, path_element, ';');
176 while (notfound && !path_element.empty()) {
177 path_element = os::slashify_path(path_element);
178 if (!suffixIs(path_element, '/'))
180 path_element = subst(path_element, "$$LyX", system_lyxdir);
181 path_element = subst(path_element, "$$User", user_lyxdir);
183 real_file = FileSearch(path_element, name, ext);
185 if (real_file.empty()) {
187 tmppath = split(tmppath, path_element, ';');
188 } while (!tmppath.empty() && path_element.empty());
194 if (ext.empty() && notfound) {
195 real_file = FileOpenSearch(path, name, "exe");
196 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
203 /// Returns a vector of all files in directory dir having extension ext.
204 vector<string> const DirList(string const & dir, string const & ext)
206 // This is a non-error checking C/system implementation
207 string extension(ext);
208 if (!extension.empty() && extension[0] != '.')
209 extension.insert(0, ".");
210 vector<string> dirlist;
211 DIR * dirp = ::opendir(dir.c_str());
214 << "Directory \"" << dir
215 << "\" does not exist to DirList." << endl;
220 while ((dire = ::readdir(dirp))) {
221 string const fil = dire->d_name;
222 if (suffixIs(fil, extension)) {
223 dirlist.push_back(fil);
228 /* I would have prefered to take a vector<string>& as parameter so
229 that we could avoid the copy of the vector when returning.
231 dirlist.swap(argvec);
232 to avoid the copy. (Lgb)
234 /* A C++ implementaion will look like this:
235 string extension(ext);
236 if (extension[0] != '.') extension.insert(0, ".");
237 vector<string> dirlist;
238 directory_iterator dit("dir");
239 while (dit != directory_iterator()) {
240 string fil = dit->filename;
241 if (prefixIs(fil, extension)) {
242 dirlist.push_back(fil);
246 dirlist.swap(argvec);
252 // Returns the real name of file name in directory path, with optional
254 string const FileSearch(string const & path, string const & name,
257 // if `name' is an absolute path, we ignore the setting of `path'
258 // Expand Environmentvariables in 'name'
259 string const tmpname = ReplaceEnvironmentPath(name);
260 string fullname = MakeAbsPath(tmpname, path);
261 // search first without extension, then with it.
262 if (IsFileReadable(fullname))
264 else if (ext.empty())
266 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
269 if (IsFileReadable(fullname))
277 // Search the file name.ext in the subdirectory dir of
279 // 2) build_lyxdir (if not empty)
281 string const LibFileSearch(string const & dir, string const & name,
284 string fullname = FileSearch(AddPath(user_lyxdir, dir), name, ext);
285 if (!fullname.empty())
288 if (!build_lyxdir.empty())
289 fullname = FileSearch(AddPath(build_lyxdir, dir), name, ext);
290 if (!fullname.empty())
293 return FileSearch(AddPath(system_lyxdir, dir), name, ext);
298 i18nLibFileSearch(string const & dir, string const & name,
301 // this comment is from intl/dcigettext.c. We try to mimick this
303 /* The highest priority value is the `LANGUAGE' environment
304 variable. But we don't use the value if the currently
305 selected locale is the C locale. This is a GNU extension. */
307 string const lc_all = GetEnv("LC_ALL");
308 string lang = GetEnv("LANGUAGE");
309 if (lang.empty() || lc_all == "C") {
312 lang = GetEnv("LANG");
316 lang = token(lang, '_', 0);
318 if (lang.empty() || lang == "C")
319 return LibFileSearch(dir, name, ext);
321 string const tmp = LibFileSearch(dir, lang + '_' + name,
326 return LibFileSearch(dir, name, ext);
331 string const GetEnv(string const & envname)
333 // f.ex. what about error checking?
334 char const * const ch = getenv(envname.c_str());
335 string const envstr = !ch ? "" : ch;
340 string const GetEnvPath(string const & name)
343 string const pathlist = subst(GetEnv(name), ':', ';');
345 string const pathlist = os::slashify_path(GetEnv(name));
347 return strip(pathlist, ';');
351 bool PutEnv(string const & envstr)
353 // CHECK Look at and fix this.
354 // f.ex. what about error checking?
357 // this leaks, but what can we do about it?
358 // Is doing a getenv() and a free() of the older value
359 // a good idea? (JMarc)
360 // Actually we don't have to leak...calling putenv like this
361 // should be enough: ... and this is obviously not enough if putenv
362 // does not make a copy of the string. It is also not very wise to
363 // put a string on the free store. If we have to leak we should do it
365 char * leaker = new char[envstr.length() + 1];
366 envstr.copy(leaker, envstr.length());
367 leaker[envstr.length()] = '\0';
368 int const retval = lyx::putenv(leaker);
370 // If putenv does not make a copy of the char const * this
371 // is very dangerous. OTOH if it does take a copy this is the
373 // The only implementation of putenv that I have seen does not
374 // allocate memory. _And_ after testing the putenv in glibc it
375 // seems that we need to make a copy of the string contents.
376 // I will enable the above.
377 //int retval = lyx::putenv(envstr.c_str());
381 string const str = envstr.split(varname,'=');
382 int const retval = ::setenv(varname.c_str(), str.c_str(), true);
384 // No environment setting function. Can this happen?
385 int const retval = 1; //return an error condition.
392 bool PutEnvPath(string const & envstr)
394 return PutEnv(envstr);
400 int DeleteAllFilesInDir (string const & path)
402 // I have decided that we will be using parts from the boost
403 // library. Check out http://www.boost.org/
404 // For directory access we will then use the directory_iterator.
405 // Then the code will be something like:
406 // directory_iterator dit(path);
407 // directory_iterator dend;
408 // if (dit == dend) {
409 // Alert::err_alert(_("Error! Cannot open directory:"), path);
412 // for (; dit != dend; ++dit) {
413 // string filename(*dit);
414 // if (filename == "." || filename == "..")
416 // string unlinkpath(AddName(path, filename));
417 // if (lyx::unlink(unlinkpath))
418 // Alert::err_alert(_("Error! Could not remove file:"),
422 DIR * dir = ::opendir(path.c_str());
424 Alert::err_alert (_("Error! Cannot open directory:"), path);
428 int return_value = 0;
429 while ((de = readdir(dir))) {
430 string const temp = de->d_name;
431 if (temp == "." || temp == "..")
433 string const unlinkpath = AddName (path, temp);
435 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
439 FileInfo fi(unlinkpath);
440 if (fi.isOK() && fi.isDir())
441 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
442 deleted &= (lyx::unlink(unlinkpath) == 0);
444 Alert::err_alert(_("Error! Could not remove file:"),
454 string const CreateTmpDir(string const & tempdir, string const & mask)
457 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
458 << "CreateTmpDir: mask=`" << mask << "'" << endl;
460 string const tmpfl(lyx::tempName(tempdir, mask));
461 // lyx::tempName actually creates a file to make sure that it
462 // stays unique. So we have to delete it before we can create
463 // a dir with the same name. Note also that we are not thread
464 // safe because of the gap between unlink and mkdir. (Lgb)
465 lyx::unlink(tmpfl.c_str());
467 if (tmpfl.empty() || lyx::mkdir(tmpfl, 0700)) {
468 Alert::err_alert(_("Error! Couldn't create temporary directory:"),
472 return MakeAbsPath(tmpfl);
476 int DestroyTmpDir(string const & tmpdir, bool Allfiles)
481 if (Allfiles && DeleteAllFilesInDir(tmpdir)) {
484 if (lyx::rmdir(tmpdir)) {
485 Alert::err_alert(_("Error! Couldn't delete temporary directory:"),
495 string const CreateBufferTmpDir(string const & pathfor)
498 static string const tmpdir(pathfor.empty() ? os::getTmpDir() : pathfor);
499 // We are in our own directory. Why bother to mangle name?
500 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
502 string const tmpfl = tmpdir + "/lyx_tmpbuf" + tostr(count++);
503 if (lyx::mkdir(tmpfl, 0777)) {
504 Alert::err_alert(_("Error! Couldn't create temporary directory:"),
512 int DestroyBufferTmpDir(string const & tmpdir)
514 return DestroyTmpDir(tmpdir, true);
518 string const CreateLyXTmpDir(string const & deflt)
520 if ((!deflt.empty()) && (deflt != "/tmp")) {
521 if (lyx::mkdir(deflt, 0777)) {
525 return CreateTmpDir(deflt, "lyx_tmpdir");
532 return CreateTmpDir("/tmp", "lyx_tmpdir");
537 int DestroyLyXTmpDir(string const & tmpdir)
539 return DestroyTmpDir (tmpdir, false); // Why false?
543 // Creates directory. Returns true if succesfull
544 bool createDirectory(string const & path, int permission)
546 string temp(strip(os::slashify_path(path), '/'));
549 Alert::alert(_("Internal error!"),
550 _("Call to createDirectory with invalid name"));
554 if (lyx::mkdir(temp, permission)) {
555 Alert::err_alert (_("Error! Couldn't create directory:"), temp);
562 // Strip filename from path name
563 string const OnlyPath(string const & Filename)
565 // If empty filename, return empty
566 if (Filename.empty()) return Filename;
568 // Find last / or start of filename
569 string::size_type j = Filename.rfind('/');
570 if (j == string::npos)
572 return Filename.substr(0, j + 1);
576 // Convert relative path into absolute path based on a basepath.
577 // If relpath is absolute, just use that.
578 // If basepath is empty, use CWD as base.
579 string const MakeAbsPath(string const & RelPath, string const & BasePath)
581 // checks for already absolute path
582 if (os::is_absolute_path(RelPath))
585 // Copies given paths
586 string TempRel(os::slashify_path(RelPath));
587 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
588 TempRel = subst(TempRel, "//", "/");
592 if (os::is_absolute_path(BasePath))
595 TempBase = AddPath(lyx::getcwd(), BasePath);
597 // Handle /./ at the end of the path
598 while (suffixIs(TempBase, "/./"))
599 TempBase.erase(TempBase.length() - 2);
601 // processes relative path
602 string RTemp(TempRel);
605 while (!RTemp.empty()) {
607 RTemp = split(RTemp, Temp, '/');
609 if (Temp == ".") continue;
611 // Remove one level of TempBase
612 string::difference_type i = TempBase.length() - 2;
615 while (i > 0 && TempBase[i] != '/') --i;
619 while (i > 2 && TempBase[i] != '/') --i;
622 TempBase.erase(i, string::npos);
625 } else if (Temp.empty() && !RTemp.empty()) {
626 TempBase = os::current_root() + RTemp;
629 // Add this piece to TempBase
630 if (!suffixIs(TempBase, '/'))
636 // returns absolute path
637 return os::slashify_path(TempBase);
641 // Correctly append filename to the pathname.
642 // If pathname is '.', then don't use pathname.
643 // Chops any path of filename.
644 string const AddName(string const & path, string const & fname)
647 string const basename(OnlyFilename(fname));
651 if (path != "." && path != "./" && !path.empty()) {
652 buf = os::slashify_path(path);
653 if (!suffixIs(path, '/'))
657 return buf + basename;
661 // Strips path from filename
662 string const OnlyFilename(string const & fname)
667 string::size_type j = fname.rfind('/');
668 if (j == string::npos) // no '/' in fname
672 return fname.substr(j + 1);
676 /// Returns true is path is absolute
677 bool AbsolutePath(string const & path)
679 return os::is_absolute_path(path);
684 // Create absolute path. If impossible, don't do anything
685 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
686 string const ExpandPath(string const & path)
688 // checks for already absolute path
689 string RTemp(ReplaceEnvironmentPath(path));
690 if (os::is_absolute_path(RTemp))
694 string const copy(RTemp);
697 RTemp = split(RTemp, Temp, '/');
700 return lyx::getcwd() /*GetCWD()*/ + '/' + RTemp;
703 return GetEnvPath("HOME") + '/' + RTemp;
706 return MakeAbsPath(copy);
708 // Don't know how to handle this
714 // Constracts path/../path
715 // Can't handle "../../" or "/../" (Asger)
716 string const NormalizePath(string const & path)
722 if (os::is_absolute_path(path))
725 // Make implicit current directory explicit
728 while (!RTemp.empty()) {
730 RTemp = split(RTemp, Temp, '/');
734 } else if (Temp == "..") {
735 // Remove one level of TempBase
736 string::difference_type i = TempBase.length() - 2;
737 while (i > 0 && TempBase[i] != '/')
739 if (i >= 0 && TempBase[i] == '/')
740 TempBase.erase(i + 1, string::npos);
744 TempBase += Temp + '/';
748 // returns absolute path
753 string const GetFileContents(string const & fname)
755 FileInfo finfo(fname);
757 ifstream ifs(fname.c_str());
762 return ofs.str().c_str();
765 lyxerr << "LyX was not able to read file '" << fname << "'" << endl;
771 // Search ${...} as Variable-Name inside the string and replace it with
772 // the denoted environmentvariable
773 // Allow Variables according to
774 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
777 string const ReplaceEnvironmentPath(string const & path)
780 // CompareChar: Environmentvariables starts with this character
781 // PathChar: Next path component start with this character
782 // while CompareChar found do:
783 // Split String with PathChar
784 // Search Environmentvariable
785 // if found: Replace Strings
787 char const CompareChar = '$';
788 char const FirstChar = '{';
789 char const EndChar = '}';
790 char const UnderscoreChar = '_';
791 string EndString; EndString += EndChar;
792 string FirstString; FirstString += FirstChar;
793 string CompareString; CompareString += CompareChar;
794 string const RegExp("*}*"); // Exist EndChar inside a String?
796 // first: Search for a '$' - Sign.
798 string result1; //(copy); // for split-calls
799 string result0 = split(path, result1, CompareChar);
800 while (!result0.empty()) {
801 string copy1(result0); // contains String after $
803 // Check, if there is an EndChar inside original String.
805 if (!regexMatch(copy1, RegExp)) {
806 // No EndChar inside. So we are finished
807 result1 += CompareString + result0;
813 string res0 = split(copy1, res1, EndChar);
814 // Now res1 holds the environmentvariable
815 // First, check, if Contents is ok.
816 if (res1.empty()) { // No environmentvariable. Continue Loop.
817 result1 += CompareString + FirstString;
821 // check contents of res1
822 char const * res1_contents = res1.c_str();
823 if (*res1_contents != FirstChar) {
824 // Again No Environmentvariable
825 result1 += CompareString;
829 // Check for variable names
830 // Situation ${} is detected as "No Environmentvariable"
831 char const * cp1 = res1_contents + 1;
832 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
834 while (*cp1 && result) {
835 result = isalnum(*cp1) ||
836 (*cp1 == UnderscoreChar);
841 // no correct variable name
842 result1 += CompareString + res1 + EndString;
843 result0 = split(res0, res1, CompareChar);
848 string env(GetEnv(res1_contents + 1));
850 // Congratulations. Environmentvariable found
853 result1 += CompareString + res1 + EndString;
856 result0 = split(res0, res1, CompareChar);
860 } // ReplaceEnvironmentPath
863 // Make relative path out of two absolute paths
864 string const MakeRelPath(string const & abspath, string const & basepath)
865 // Makes relative path out of absolute path. If it is deeper than basepath,
866 // it's easy. If basepath and abspath share something (they are all deeper
867 // than some directory), it'll be rendered using ..'s. If they are completely
868 // different, then the absolute path will be used as relative path.
870 string::size_type const abslen = abspath.length();
871 string::size_type const baselen = basepath.length();
873 string::size_type i = os::common_path(abspath, basepath);
876 // actually no match - cannot make it relative
880 // Count how many dirs there are in basepath above match
881 // and append as many '..''s into relpath
883 string::size_type j = i;
884 while (j < baselen) {
885 if (basepath[j] == '/') {
886 if (j + 1 == baselen)
893 // Append relative stuff from common directory to abspath
894 if (abspath[i] == '/')
896 for (; i < abslen; ++i)
899 if (suffixIs(buf, '/'))
900 buf.erase(buf.length() - 1);
901 // Substitute empty with .
908 // Append sub-directory(ies) to a path in an intelligent way
909 string const AddPath(string const & path, string const & path_2)
912 string const path2 = os::slashify_path(path_2);
914 if (!path.empty() && path != "." && path != "./") {
915 buf = os::slashify_path(path);
916 if (path[path.length() - 1] != '/')
920 if (!path2.empty()) {
921 string::size_type const p2start = path2.find_first_not_of('/');
922 string::size_type const p2end = path2.find_last_not_of('/');
923 string const tmp = path2.substr(p2start, p2end - p2start + 1);
931 Change extension of oldname to extension.
932 Strips path off if no_path == true.
933 If no extension on oldname, just appends.
935 string const ChangeExtension(string const & oldname, string const & extension)
937 string::size_type const last_slash = oldname.rfind('/');
938 string::size_type last_dot = oldname.rfind('.');
939 if (last_dot < last_slash && last_slash != string::npos)
940 last_dot = string::npos;
943 // Make sure the extension starts with a dot
944 if (!extension.empty() && extension[0] != '.')
945 ext= "." + extension;
949 return os::slashify_path(oldname.substr(0, last_dot) + ext);
953 /// Return the extension of the file (not including the .)
954 string const GetExtension(string const & name)
956 string::size_type const last_slash = name.rfind('/');
957 string::size_type const last_dot = name.rfind('.');
958 if (last_dot != string::npos &&
959 (last_slash == string::npos || last_dot > last_slash))
960 return name.substr(last_dot + 1,
961 name.length() - (last_dot + 1));
966 // the different filetypes and what they contain in one of the first lines
967 // (dots are any characters). (Herbert 20020131)
968 // EPS %!PS-Adobe-3.0 EPSF...
974 // XBM ... static char ...
977 // GZIP \213\037\008\008... http://www.ietf.org/rfc/rfc1952.txt
978 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
979 // Z \177\037 UNIX compress
980 /// return the "extension" which belongs to the contents
981 string const getExtFromContents(string const & filename) {
982 if (filename.empty() || !IsFileReadable(filename))
983 return string(); // paranoia check
984 ifstream ifs(filename.c_str());
986 return string(); // Couldn't open file...
987 string const gzipStamp = "\213\037\008\008"; // gnuzip
988 string const zipStamp = "PK"; // PKZIP
989 string const compressStamp = "\177\037"; // compress
990 int const max_count = 50; // Maximum strings to read to attempt recognition
991 int count = 0; // Counter of attempts.
993 bool zipChecked = false;
994 for (; count < max_count; ++count) {
996 lyxerr[Debug::INFO] << "InsetGraphics (classifyFiletype)"
997 " End of file reached and it wasn't found to be a known Type!" << endl;
1002 // at first we check for a zipped file, because this information
1003 // is saved in the first bytes of the file!
1004 if (str.substr(0,4) == gzipStamp)
1006 else if (str.substr(0,2) == zipStamp)
1008 else if (str.substr(0,2) == compressStamp)
1012 if (contains(str,"EPSF"))
1014 else if (contains(str,"GIF"))
1016 else if (contains(str,"JFIF"))
1018 else if (contains(str,"%PDF"))
1020 else if (contains(str,"PNG"))
1022 else if (contains(str,"%!PS-Adobe-"))
1023 return "ps"; // eps here no more possible
1024 else if (contains(str,"static char"))
1026 else if (contains(str,"XPM"))
1029 lyxerr[Debug::INFO] << "InsetGraphics (classifyFiletype)"
1030 " Couldn't find a known Type!" << endl;
1035 /// check for zipped file
1036 bool zippedFile(string const & name) {
1037 string const type = getExtFromContents(name);
1038 if (contains("gzip zip",type) && !type.empty())
1043 // Creates a nice compact path for displaying
1045 MakeDisplayPath (string const & path, unsigned int threshold)
1047 string::size_type const l1 = path.length();
1049 // First, we try a relative path compared to home
1050 string const home(GetEnvPath("HOME"));
1051 string relhome = MakeRelPath(path, home);
1053 string::size_type l2 = relhome.length();
1057 // If we backup from home or don't have a relative path,
1058 // this try is no good
1059 if (prefixIs(relhome, "../") || os::is_absolute_path(relhome)) {
1060 // relative path was no good, just use the original path
1067 // Is the path too long?
1068 if (l2 > threshold) {
1074 while (relhome.length() > threshold)
1075 relhome = split(relhome, temp, '/');
1077 // Did we shortend everything away?
1078 if (relhome.empty()) {
1079 // Yes, filename in itself is too long.
1080 // Pick the start and the end of the filename.
1081 relhome = OnlyFilename(path);
1082 string const head = relhome.substr(0, threshold/2 - 3);
1084 l2 = relhome.length();
1086 relhome.substr(l2 - threshold/2 - 2, l2 - 1);
1087 relhome = head + "..." + tail;
1090 return prefix + relhome;
1094 bool LyXReadLink(string const & File, string & Link)
1096 char LinkBuffer[512];
1097 // Should be PATH_MAX but that needs autconf support
1098 int const nRead = ::readlink(File.c_str(),
1099 LinkBuffer, sizeof(LinkBuffer) - 1);
1102 LinkBuffer[nRead] = '\0'; // terminator
1110 typedef pair<int, string> cmdret;
1112 cmdret const do_popen(string const & cmd)
1114 // One question is if we should use popen or
1115 // create our own popen based on fork, exec, pipe
1116 // of course the best would be to have a
1117 // pstream (process stream), with the
1118 // variants ipstream, opstream
1119 FILE * inf = ::popen(cmd.c_str(), "r");
1123 ret += static_cast<char>(c);
1126 int const pret = pclose(inf);
1127 return make_pair(pret, ret);
1133 string const findtexfile(string const & fil, string const & /*format*/)
1135 /* There is no problem to extend this function too use other
1136 methods to look for files. It could be setup to look
1137 in environment paths and also if wanted as a last resort
1138 to a recursive find. One of the easier extensions would
1139 perhaps be to use the LyX file lookup methods. But! I am
1140 going to implement this until I see some demand for it.
1144 // If the file can be found directly, we just return a
1145 // absolute path version of it.
1146 if (FileInfo(fil).exist())
1147 return MakeAbsPath(fil);
1149 // No we try to find it using kpsewhich.
1150 // It seems from the kpsewhich manual page that it is safe to use
1151 // kpsewhich without --format: "When the --format option is not
1152 // given, the search path used when looking for a file is inferred
1153 // from the name given, by looking for a known extension. If no
1154 // known extension is found, the search path for TeX source files
1156 // However, we want to take advantage of the format sine almost all
1157 // the different formats has environment variables that can be used
1158 // to controll which paths to search. f.ex. bib looks in
1159 // BIBINPUTS and TEXBIB. Small list follows:
1160 // bib - BIBINPUTS, TEXBIB
1162 // graphic/figure - TEXPICTS, TEXINPUTS
1163 // ist - TEXINDEXSTYLE, INDEXSTYLE
1164 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1166 // tfm - TFMFONTS, TEXFONTS
1167 // This means that to use kpsewhich in the best possible way we
1168 // should help it by setting additional path in the approp. envir.var.
1169 string const kpsecmd = "kpsewhich " + fil;
1171 cmdret const c = do_popen(kpsecmd);
1173 lyxerr[Debug::LATEX] << "kpse status = " << c.first << "\n"
1174 << "kpse result = `" << strip(c.second, '\n')
1177 return os::internal_path(strip(strip(c.second, '\n'), '\r'));
1183 void removeAutosaveFile(string const & filename)
1185 string a = OnlyPath(filename);
1187 a += OnlyFilename(filename);
1189 FileInfo const fileinfo(a);
1190 if (fileinfo.exist()) {
1191 if (lyx::unlink(a) != 0) {
1192 Alert::err_alert(_("Could not delete auto-save file!"), a);