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"
30 #include "filetools.h"
31 #include "LSubstring.h"
32 #include "lyx_gui_misc.h"
34 #include "support/path.h" // I know it's OS/2 specific (SMiyata)
35 #include "support/syscall.h"
39 // Which part of this is still necessary? (JMarc).
42 # define NAMLEN(dirent) strlen((dirent)->d_name)
44 # define dirent direct
45 # define NAMLEN(dirent) (dirent)->d_namlen
47 # include <sys/ndir.h>
70 extern string system_lyxdir;
71 extern string build_lyxdir;
72 extern string user_lyxdir;
73 extern string system_tempdir;
76 bool IsLyXFilename(string const & filename)
78 return contains(filename, ".lyx");
82 // Substitutes spaces with underscores in filename (and path)
83 string const MakeLatexName(string const & file)
85 string name = OnlyFilename(file);
86 string const path = OnlyPath(file);
88 for (string::size_type i = 0; i < name.length(); ++i) {
89 name[i] &= 0x7f; // set 8th bit to 0
92 // ok so we scan through the string twice, but who cares.
93 string const keep("abcdefghijklmnopqrstuvwxyz"
94 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
95 "@!\"'()*+,-./0123456789:;<=>?[]`|");
97 string::size_type pos = 0;
98 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
101 return AddName(path, name);
105 // Substitutes spaces with underscores in filename (and path)
106 string const QuoteName(string const & name)
108 // CHECK Add proper emx support here!
110 return "\'" + name + "\'";
118 // Returns an unique name to be used as a temporary file.
119 string const TmpFileName(string const & dir, string const & mask)
120 {// With all these temporary variables, it should be safe enough :-) (JMarc)
123 tmpdir = system_tempdir;
126 string tmpfl(AddName(tmpdir, mask));
128 // find a uniq postfix for the filename...
129 // using the pid, and...
130 tmpfl += tostr(getpid());
134 for (int a = 'a'; a <= 'z'; ++a)
135 for (int b = 'a'; b <= 'z'; ++b)
136 for (int c = 'a'; c <= 'z'; ++c) {
137 // if this is not enough I have no idea what
139 ret = tmpfl + char(a) + char(b) + char(c);
140 // check if the file exist
141 if (!fnfo.newFile(ret).exist())
144 lyxerr << "Not able to find a uniq tmpfile name." << endl;
150 // Is a file readable ?
151 bool IsFileReadable (string const & path)
154 if (file.isOK() && file.isRegular() && file.readable())
161 // Is a file read_only?
162 // return 1 read-write
164 // -1 error (doesn't exist, no access, anything else)
165 int IsFileWriteable (string const & path)
168 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
170 if (fi.readable()) // read-only
172 return -1; // everything else.
176 //returns 1: dir writeable
178 // -1: error- couldn't find out
179 int IsDirWriteable (string const & path)
181 string const tmpfl(lyx::tempName(path)); //TmpFileName(path));
184 WriteFSAlert(_("LyX Internal Error!"),
185 _("Could not test if directory is writeable"));
189 if (fi.writable()) return 1;
195 // Uses a string of paths separated by ";"s to find a file to open.
196 // Can't cope with pathnames with a ';' in them. Returns full path to file.
197 // If path entry begins with $$LyX/, use system_lyxdir
198 // If path entry begins with $$User/, use user_lyxdir
199 // Example: "$$User/doc;$$LyX/doc"
200 string const FileOpenSearch (string const & path, string const & name,
203 string real_file, path_element;
204 bool notfound = true;
205 string tmppath = split(path, path_element, ';');
207 while (notfound && !path_element.empty()) {
208 path_element = CleanupPath(path_element);
209 if (!suffixIs(path_element, '/'))
211 path_element = subst(path_element, "$$LyX", system_lyxdir);
212 path_element = subst(path_element, "$$User", user_lyxdir);
214 real_file = FileSearch(path_element, name, ext);
216 if (real_file.empty()) {
218 tmppath = split(tmppath, path_element, ';');
219 } while(!tmppath.empty() && path_element.empty());
225 if (ext.empty() && notfound) {
226 real_file = FileOpenSearch(path, name, "exe");
227 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
234 /// Returns a vector of all files in directory dir having extension ext.
235 vector<string> const DirList( string const & dir, string const & ext)
237 // What what what????
238 // where you tinking when you implemented this?
240 string lsCommand = "ls " + dir;
242 string::size_type sz = lsCommand.size();
243 if (lsCommand[sz - 1] != '/')
250 string tmpfile = system_tempdir + "/dirlist";
251 lsCommand += " > " + tmpfile;
253 Systemcalls(Systemcalls::System, lsCommand);
255 string contents = GetFileContents(tmpfile);
256 string rmCommand = "rm " + tmpfile;
257 Systemcalls(Systemcalls::System, rmCommand);
259 string tmp = strip(contents);
260 vector<string> dirlist;
262 while (!tmp.empty()) {
264 tmp = frontStrip(split(tmp, file, '\n'));
265 dirlist.push_back( file );
269 // This is a non-error checking C/system implementation
271 string extension(ext);
272 if (extension[0] != '.')
273 // If I do not use the explicit cast below, compaq cxx
274 // is not able to guess between
275 // insert(size_type, size_type, value_type)
277 // insert(iterator, size_type, value_type)
278 extension.insert(string::size_type(0), 1u, '.');
279 vector<string> dirlist;
280 DIR * dirp = ::opendir(dir.c_str());
282 while ((dire = ::readdir(dirp))) {
283 string fil = dire->d_name;
284 if (prefixIs(fil, extension)) {
285 dirlist.push_back(fil);
290 /* A C++ implementaion will look like this:
291 if (ext[0] != '.') ext.insert(0u, 1u, '.');
292 directory_iterator dit("dir");
293 while (dit != directory_iterator()) {
294 string fil = (*dit).filename;
295 if (prefixIs(fil, ext)) {
296 dirlist.push_back(fil);
305 // Returns the real name of file name in directory path, with optional
307 string const FileSearch(string const & path, string const & name,
310 // if `name' is an absolute path, we ignore the setting of `path'
311 // Expand Environmentvariables in 'name'
312 string const tmpname = ReplaceEnvironmentPath(name);
313 string fullname = MakeAbsPath(tmpname, path);
315 // search first without extension, then with it.
316 if (IsFileReadable(fullname))
318 else if (ext.empty())
320 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
323 if (IsFileReadable(fullname))
331 // Search the file name.ext in the subdirectory dir of
333 // 2) build_lyxdir (if not empty)
335 string const LibFileSearch(string const & dir, string const & name,
338 string fullname = FileSearch(AddPath(user_lyxdir, dir),
340 if (!fullname.empty())
343 if (!build_lyxdir.empty())
344 fullname = FileSearch(AddPath(build_lyxdir, dir),
346 if (!fullname.empty())
349 return FileSearch(AddPath(system_lyxdir, dir), name, ext);
354 i18nLibFileSearch(string const & dir, string const & name,
357 string const lang = token(string(GetEnv("LANG")), '_', 0);
359 if (lang.empty() || lang == "C")
360 return LibFileSearch(dir, name, ext);
362 string const tmp = LibFileSearch(dir, lang + '_' + name,
367 return LibFileSearch(dir, name, ext);
372 string const GetEnv(string const & envname)
374 // f.ex. what about error checking?
375 char const * const ch = getenv(envname.c_str());
376 string const envstr = !ch ? "" : ch;
381 string const GetEnvPath(string const & name)
384 string const pathlist = subst(GetEnv(name), ':', ';');
386 string const pathlist = subst(GetEnv(name), '\\', '/');
388 return strip(pathlist, ';');
392 bool PutEnv(string const & envstr)
394 // CHECK Look at and fix this.
395 // f.ex. what about error checking?
398 // this leaks, but what can we do about it?
399 // Is doing a getenv() and a free() of the older value
400 // a good idea? (JMarc)
401 // Actually we don't have to leak...calling putenv like this
402 // should be enough: ... and this is obviously not enough if putenv
403 // does not make a copy of the string. It is also not very wise to
404 // put a string on the free store. If we have to leak we should do it
406 char * leaker = new char[envstr.length() + 1];
407 envstr.copy(leaker, envstr.length());
408 leaker[envstr.length()] = '\0';
409 int const retval = lyx::putenv(leaker);
411 // If putenv does not make a copy of the char const * this
412 // is very dangerous. OTOH if it does take a copy this is the
414 // The only implementation of putenv that I have seen does not
415 // allocate memory. _And_ after testing the putenv in glibc it
416 // seems that we need to make a copy of the string contents.
417 // I will enable the above.
418 //int retval = lyx::putenv(envstr.c_str());
422 string const str = envstr.split(varname,'=');
423 int const retval = ::setenv(varname.c_str(), str.c_str(), true);
425 // No environment setting function. Can this happen?
426 int const retval = 1; //return an error condition.
433 bool PutEnvPath(string const & envstr)
435 return PutEnv(envstr);
440 int DeleteAllFilesInDir (string const & path)
442 // I have decided that we will be using parts from the boost
443 // library. Check out http://www.boost.org/
444 // For directory access we will then use the directory_iterator.
445 // Then the code will be something like:
446 // directory_iterator dit(path);
447 // directory_iterator dend;
448 // if (dit == dend) {
449 // WriteFSAlert(_("Error! Cannot open directory:"), path);
452 // for (; dit != dend; ++dit) {
453 // string filename(*dit);
454 // if (filename == "." || filename == "..")
456 // string unlinkpath(AddName(path, filename));
457 // if (lyx::unlink(unlinkpath))
458 // WriteFSAlert(_("Error! Could not remove file:"),
462 DIR * dir = ::opendir(path.c_str());
464 WriteFSAlert (_("Error! Cannot open directory:"), path);
468 int return_value = 0;
469 while ((de = readdir(dir))) {
470 string const temp = de->d_name;
471 if (temp == "." || temp == "..")
473 string const unlinkpath = AddName (path, temp);
475 lyxerr.debug() << "Deleting file: " << unlinkpath << endl;
478 if (FileInfo(unlinkpath).isDir())
479 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
480 deleted &= (lyx::unlink(unlinkpath) == 0);
482 WriteFSAlert (_("Error! Could not remove file:"),
493 string const CreateTmpDir(string const & tempdir, string const & mask)
495 #warning Possibly buggy (Lgb)
496 lyxerr << "CreateTmpDir: tempdir=`" << tempdir << "'" << endl;
497 lyxerr << "CreateTmpDir: mask=`" << mask << "'" << endl;
499 string const tmpfl(lyx::tempName(tempdir, mask));
500 // lyx::tempName actually creates a file to make sure that it
501 // stays unique. So we have to delete it before we can create
502 // a dir with the same name. Note also that we are not thread
503 // safe because of the gap between unlink and mkdir. (Lgb)
504 lyx::unlink(tmpfl.c_str());
506 if (tmpfl.empty() || lyx::mkdir(tmpfl, 0777)) {
507 WriteFSAlert(_("Error! Couldn't create temporary directory:"),
511 return MakeAbsPath(tmpfl);
516 int DestroyTmpDir(string const & tmpdir, bool Allfiles)
521 if (Allfiles && DeleteAllFilesInDir(tmpdir)) return -1;
522 if (lyx::rmdir(tmpdir)) {
523 WriteFSAlert(_("Error! Couldn't delete temporary directory:"),
531 string const CreateBufferTmpDir(string const & pathfor)
533 return CreateTmpDir(pathfor, "lyx_tmpbuf");
537 int DestroyBufferTmpDir(string const & tmpdir)
539 return DestroyTmpDir(tmpdir, true);
543 string const CreateLyXTmpDir(string const & deflt)
545 if ((!deflt.empty()) && (deflt != "/tmp")) {
546 if (lyx::mkdir(deflt, 0777)) {
550 string const t(CreateTmpDir(deflt, "lyx_tmpdir"));
558 string const t(CreateTmpDir("/tmp", "lyx_tmpdir"));
564 int DestroyLyXTmpDir (string const & tmpdir)
566 return DestroyTmpDir (tmpdir, false); // Why false?
570 // Creates directory. Returns true if succesfull
571 bool createDirectory(string const & path, int permission)
573 string temp(strip(CleanupPath(path), '/'));
576 WriteAlert(_("Internal error!"),
577 _("Call to createDirectory with invalid name"));
581 if (lyx::mkdir(temp, permission)) {
582 WriteFSAlert (_("Error! Couldn't create directory:"), temp);
590 // Returns current working directory
591 string const GetCWD ()
593 int n = 256; // Assume path is less than 256 chars
595 char * tbuf = new char[n];
597 // Safe. Hopefully all getcwds behave this way!
598 while (((err = lyx::getcwd(tbuf, n)) == 0) && (errno == ERANGE)) {
599 // Buffer too small, double the buffersize and try again
606 if (err) result = tbuf;
613 // Strip filename from path name
614 string const OnlyPath(string const & Filename)
616 // If empty filename, return empty
617 if (Filename.empty()) return Filename;
619 // Find last / or start of filename
620 string::size_type j = Filename.rfind('/');
621 if (j == string::npos)
623 return Filename.substr(0, j + 1);
627 // Convert relative path into absolute path based on a basepath.
628 // If relpath is absolute, just use that.
629 // If basepath is empty, use CWD as base.
630 string const MakeAbsPath(string const & RelPath, string const & BasePath)
632 // checks for already absolute path
633 if (AbsolutePath(RelPath))
635 if (RelPath[0]!= '/' && RelPath[0]!= '\\')
639 // Copies given paths
640 string TempRel(CleanupPath(RelPath));
644 if (!BasePath.empty()) {
648 char * with_drive = new char[_MAX_PATH];
649 _abspath(with_drive, BasePath.c_str(), _MAX_PATH);
650 TempBase = with_drive;
654 TempBase = lyx::getcwd(); //GetCWD();
656 if (AbsolutePath(TempRel))
657 return TempBase.substr(0, 2) + TempRel;
660 // Handle /./ at the end of the path
661 while(suffixIs(TempBase, "/./"))
662 TempBase.erase(TempBase.length() - 2);
664 // processes relative path
665 string RTemp(TempRel);
668 while (!RTemp.empty()) {
670 RTemp = split(RTemp, Temp, '/');
672 if (Temp == ".") continue;
674 // Remove one level of TempBase
675 string::difference_type i = TempBase.length() - 2;
678 while (i > 0 && TempBase[i] != '/') --i;
682 while (i > 2 && TempBase[i] != '/') --i;
685 TempBase.erase(i, string::npos);
689 // Add this piece to TempBase
690 if (!suffixIs(TempBase, '/'))
696 // returns absolute path
701 // Correctly append filename to the pathname.
702 // If pathname is '.', then don't use pathname.
703 // Chops any path of filename.
704 string const AddName(string const & path, string const & fname)
707 string const basename(OnlyFilename(fname));
711 if (path != "." && path != "./" && !path.empty()) {
712 buf = CleanupPath(path);
713 if (!suffixIs(path, '/'))
717 return buf + basename;
721 // Strips path from filename
722 string const OnlyFilename(string const & fname)
727 string::size_type j = fname.rfind('/');
728 if (j == string::npos) // no '/' in fname
732 return fname.substr(j + 1);
736 // Is a filename/path absolute?
737 bool AbsolutePath(string const & path)
740 return (!path.empty() && path[0] == '/');
742 return (!path.empty() && (path[0] == '/' || (isalpha(static_cast<unsigned char>(path[0])) && path.length()>1 && path[1] == ':')));
747 // Create absolute path. If impossible, don't do anything
748 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
749 string const ExpandPath(string const & path)
751 // checks for already absolute path
752 string RTemp(ReplaceEnvironmentPath(path));
753 if (AbsolutePath(RTemp))
757 string const copy(RTemp);
760 RTemp = split(RTemp, Temp, '/');
763 return lyx::getcwd() /*GetCWD()*/ + '/' + RTemp;
764 } else if (Temp == "~") {
765 return GetEnvPath("HOME") + '/' + RTemp;
766 } else if (Temp == "..") {
767 return MakeAbsPath(copy);
769 // Don't know how to handle this
775 // Constracts path/../path
776 // Can't handle "../../" or "/../" (Asger)
777 string const NormalizePath(string const & path)
783 if (AbsolutePath(path))
786 // Make implicit current directory explicit
789 while (!RTemp.empty()) {
791 RTemp = split(RTemp, Temp, '/');
795 } else if (Temp == "..") {
796 // Remove one level of TempBase
797 string::difference_type i = TempBase.length() - 2;
798 while (i > 0 && TempBase[i] != '/')
800 if (i >= 0 && TempBase[i] == '/')
801 TempBase.erase(i + 1, string::npos);
805 TempBase += Temp + '/';
809 // returns absolute path
814 string const CleanupPath(string const & path)
816 #ifdef __EMX__ /* SMiyata: This should fix searchpath bug. */
817 string temppath = subst(path, '\\', '/');
818 temppath = subst(temppath, "//", "/");
819 return lowercase(temppath);
820 #else // On unix, nothing to do
826 string const GetFileContents(string const & fname)
828 FileInfo finfo(fname);
830 ifstream ifs(fname.c_str());
831 std::ostringstream ofs;
835 return ofs.str().c_str();
838 lyxerr << "LyX was not able to read file '" << fname << "'" << endl;
844 // Search ${...} as Variable-Name inside the string and replace it with
845 // the denoted environmentvariable
846 // Allow Variables according to
847 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
850 string const ReplaceEnvironmentPath(string const & path)
853 // CompareChar: Environmentvariables starts with this character
854 // PathChar: Next path component start with this character
855 // while CompareChar found do:
856 // Split String with PathChar
857 // Search Environmentvariable
858 // if found: Replace Strings
860 char const CompareChar = '$';
861 char const FirstChar = '{';
862 char const EndChar = '}';
863 char const UnderscoreChar = '_';
864 string EndString; EndString += EndChar;
865 string FirstString; FirstString += FirstChar;
866 string CompareString; CompareString += CompareChar;
867 string const RegExp("*}*"); // Exist EndChar inside a String?
869 // first: Search for a '$' - Sign.
871 string result1; //(copy); // for split-calls
872 string result0 = split(path, result1, CompareChar);
873 while (!result0.empty()) {
874 string copy1(result0); // contains String after $
876 // Check, if there is an EndChar inside original String.
878 if (!regexMatch(copy1, RegExp)) {
879 // No EndChar inside. So we are finished
880 result1 += CompareString + result0;
886 string res0 = split(copy1, res1, EndChar);
887 // Now res1 holds the environmentvariable
888 // First, check, if Contents is ok.
889 if (res1.empty()) { // No environmentvariable. Continue Loop.
890 result1 += CompareString + FirstString;
894 // check contents of res1
895 char const * res1_contents = res1.c_str();
896 if (*res1_contents != FirstChar) {
897 // Again No Environmentvariable
898 result1 += CompareString;
902 // Check for variable names
903 // Situation ${} is detected as "No Environmentvariable"
904 char const * cp1 = res1_contents + 1;
905 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
907 while (*cp1 && result) {
908 result = isalnum(*cp1) ||
909 (*cp1 == UnderscoreChar);
914 // no correct variable name
915 result1 += CompareString + res1 + EndString;
916 result0 = split(res0, res1, CompareChar);
921 string env(GetEnv(res1_contents + 1));
923 // Congratulations. Environmentvariable found
926 result1 += CompareString + res1 + EndString;
929 result0 = split(res0, res1, CompareChar);
933 } // ReplaceEnvironmentPath
936 // Make relative path out of two absolute paths
937 string const MakeRelPath(string const & abspath0, string const & basepath0)
938 // Makes relative path out of absolute path. If it is deeper than basepath,
939 // it's easy. If basepath and abspath share something (they are all deeper
940 // than some directory), it'll be rendered using ..'s. If they are completely
941 // different, then the absolute path will be used as relative path.
943 // This is a hack. It should probaly be done in another way. Lgb.
944 string abspath = CleanupPath(abspath0);
945 string basepath = CleanupPath(basepath0);
947 return "<unknown_path>";
949 string::size_type const abslen = abspath.length();
950 string::size_type const baselen = basepath.length();
952 // Find first different character
953 string::size_type i = 0;
954 while (i < abslen && i < baselen && abspath[i] == basepath[i]) ++i;
957 if (i < abslen && i < baselen
958 || (i<abslen && abspath[i] != '/' && i == baselen)
959 || (i<baselen && basepath[i] != '/' && i == abslen))
961 if (i) --i; // here was the last match
962 while (i && abspath[i] != '/') --i;
966 // actually no match - cannot make it relative
970 // Count how many dirs there are in basepath above match
971 // and append as many '..''s into relpath
973 string::size_type j = i;
974 while (j < baselen) {
975 if (basepath[j] == '/') {
976 if (j + 1 == baselen) break;
982 // Append relative stuff from common directory to abspath
983 if (abspath[i] == '/') ++i;
984 for (; i < abslen; ++i)
987 if (suffixIs(buf, '/'))
988 buf.erase(buf.length() - 1);
989 // Substitute empty with .
996 // Append sub-directory(ies) to a path in an intelligent way
997 string const AddPath(string const & path, string const & path_2)
1000 string const path2 = CleanupPath(path_2);
1002 if (!path.empty() && path != "." && path != "./") {
1003 buf = CleanupPath(path);
1004 if (path[path.length() - 1] != '/')
1008 if (!path2.empty()){
1009 string::size_type p2start = path2.find_first_not_of('/');
1011 string::size_type p2end = path2.find_last_not_of('/');
1013 string tmp = path2.substr(p2start, p2end - p2start + 1);
1021 Change extension of oldname to extension.
1022 Strips path off if no_path == true.
1023 If no extension on oldname, just appends.
1026 ChangeExtension(string const & oldname, string const & extension)
1028 string::size_type const last_slash = oldname.rfind('/');
1029 string::size_type last_dot = oldname.rfind('.');
1030 if (last_dot < last_slash && last_slash != string::npos)
1031 last_dot = string::npos;
1034 // Make sure the extension starts with a dot
1035 if (!extension.empty() && extension[0] != '.')
1036 ext= "." + extension;
1040 return CleanupPath(oldname.substr(0, last_dot) + ext);
1044 /// Return the extension of the file (not including the .)
1045 string const GetExtension(string const & name)
1047 string::size_type const last_slash = name.rfind('/');
1048 string::size_type const last_dot = name.rfind('.');
1049 if (last_dot != string::npos &&
1050 (last_slash == string::npos || last_dot > last_slash))
1051 return name.substr(last_dot + 1,
1052 name.length() - (last_dot + 1));
1058 // Creates a nice compact path for displaying
1060 MakeDisplayPath (string const & path, unsigned int threshold)
1062 string::size_type const l1 = path.length();
1064 // First, we try a relative path compared to home
1065 string const home(GetEnvPath("HOME"));
1066 string relhome = MakeRelPath(path, home);
1068 string::size_type l2 = relhome.length();
1072 // If we backup from home or don't have a relative path,
1073 // this try is no good
1074 if (prefixIs(relhome, "../") || AbsolutePath(relhome)) {
1075 // relative path was no good, just use the original path
1082 // Is the path too long?
1083 if (l2 > threshold) {
1089 while (relhome.length() > threshold)
1090 relhome = split(relhome, temp, '/');
1092 // Did we shortend everything away?
1093 if (relhome.empty()) {
1094 // Yes, filename in itself is too long.
1095 // Pick the start and the end of the filename.
1096 relhome = OnlyFilename(path);
1097 string head = relhome.substr(0, threshold/2 - 3);
1099 l2 = relhome.length();
1101 relhome.substr(l2 - threshold/2 - 2, l2 - 1);
1102 relhome = head + "..." + tail;
1105 return prefix + relhome;
1109 bool LyXReadLink(string const & File, string & Link)
1111 char LinkBuffer[512];
1112 // Should be PATH_MAX but that needs autconf support
1113 int const nRead = ::readlink(File.c_str(),
1114 LinkBuffer, sizeof(LinkBuffer) - 1);
1117 LinkBuffer[nRead] = 0;
1123 typedef pair<int, string> cmdret;
1125 cmdret const do_popen(string const & cmd)
1127 // One question is if we should use popen or
1128 // create our own popen based on fork, exec, pipe
1129 // of course the best would be to have a
1130 // pstream (process stream), with the
1131 // variants ipstream, opstream
1132 FILE * inf = ::popen(cmd.c_str(), "r");
1136 ret += static_cast<char>(c);
1139 int pret = pclose(inf);
1140 return make_pair(pret, ret);
1145 findtexfile(string const & fil, string const & /*format*/)
1147 /* There is no problem to extend this function too use other
1148 methods to look for files. It could be setup to look
1149 in environment paths and also if wanted as a last resort
1150 to a recursive find. One of the easier extensions would
1151 perhaps be to use the LyX file lookup methods. But! I am
1152 going to implement this until I see some demand for it.
1156 // If the file can be found directly, we just return a
1157 // absolute path version of it.
1158 if (FileInfo(fil).exist())
1159 return MakeAbsPath(fil);
1161 // No we try to find it using kpsewhich.
1162 // It seems from the kpsewhich manual page that it is safe to use
1163 // kpsewhich without --format: "When the --format option is not
1164 // given, the search path used when looking for a file is inferred
1165 // from the name given, by looking for a known extension. If no
1166 // known extension is found, the search path for TeX source files
1168 // However, we want to take advantage of the format sine almost all
1169 // the different formats has environment variables that can be used
1170 // to controll which paths to search. f.ex. bib looks in
1171 // BIBINPUTS and TEXBIB. Small list follows:
1172 // bib - BIBINPUTS, TEXBIB
1174 // graphic/figure - TEXPICTS, TEXINPUTS
1175 // ist - TEXINDEXSTYLE, INDEXSTYLE
1176 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1178 // tfm - TFMFONTS, TEXFONTS
1179 // This means that to use kpsewhich in the best possible way we
1180 // should help it by setting additional path in the approp. envir.var.
1181 string const kpsecmd = "kpsewhich " + fil;
1183 cmdret const c = do_popen(kpsecmd);
1185 lyxerr[Debug::LATEX] << "kpse status = " << c.first << "\n"
1186 << "kpse result = `" << strip(c.second, '\n')
1188 return c.first != -1 ? strip(c.second, '\n') : string();
1192 void removeAutosaveFile(string const & filename)
1194 string a = OnlyPath(filename);
1196 a += OnlyFilename(filename);
1198 FileInfo fileinfo(a);
1199 if (fileinfo.exist()) {
1200 if (lyx::unlink(a) != 0) {
1201 WriteFSAlert(_("Could not delete auto-save file!"), a);