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"
34 #include "support/lstrings.h"
36 #include "filetools.h"
37 #include "LSubstring.h"
38 #include "lyx_gui_misc.h"
40 #include "support/path.h" // I know it's OS/2 specific (SMiyata)
45 // Which part of this is still necessary? (JMarc).
48 # define NAMLEN(dirent) strlen((dirent)->d_name)
50 # define dirent direct
51 # define NAMLEN(dirent) (dirent)->d_namlen
53 # include <sys/ndir.h>
69 extern string system_lyxdir;
70 extern string build_lyxdir;
71 extern string user_lyxdir;
72 extern string system_tempdir;
75 bool IsLyXFilename(string const & filename)
77 return suffixIs(lowercase(filename), ".lyx");
81 bool IsSGMLFilename(string const & filename)
83 return suffixIs(lowercase(filename), ".sgml");
87 // Substitutes spaces with underscores in filename (and path)
88 string const MakeLatexName(string const & file)
90 string name = OnlyFilename(file);
91 string const path = OnlyPath(file);
93 for (string::size_type i = 0; i < name.length(); ++i) {
94 name[i] &= 0x7f; // set 8th bit to 0
97 // ok so we scan through the string twice, but who cares.
98 string const keep("abcdefghijklmnopqrstuvwxyz"
99 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
100 "@!\"'()*+,-./0123456789:;<=>?[]`|");
102 string::size_type pos = 0;
103 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
106 return AddName(path, name);
110 // Substitutes spaces with underscores in filename (and path)
111 string const QuoteName(string const & name)
113 return (os::shell() == os::UNIX) ?
119 // Is a file readable ?
120 bool IsFileReadable (string const & path)
123 if (file.isOK() && file.isRegular() && file.readable())
130 // Is a file read_only?
131 // return 1 read-write
133 // -1 error (doesn't exist, no access, anything else)
134 int IsFileWriteable (string const & path)
138 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
140 if (fi.readable()) // read-only
142 return -1; // everything else.
146 //returns true: dir writeable
147 // false: not writeable
148 bool IsDirWriteable (string const & path)
150 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
152 string const tmpfl(lyx::tempName(path, "lyxwritetest"));
162 // Uses a string of paths separated by ";"s to find a file to open.
163 // Can't cope with pathnames with a ';' in them. Returns full path to file.
164 // If path entry begins with $$LyX/, use system_lyxdir
165 // If path entry begins with $$User/, use user_lyxdir
166 // Example: "$$User/doc;$$LyX/doc"
167 string const FileOpenSearch (string const & path, string const & name,
170 string real_file, path_element;
171 bool notfound = true;
172 string tmppath = split(path, path_element, ';');
174 while (notfound && !path_element.empty()) {
175 path_element = os::slashify_path(path_element);
176 if (!suffixIs(path_element, '/'))
178 path_element = subst(path_element, "$$LyX", system_lyxdir);
179 path_element = subst(path_element, "$$User", user_lyxdir);
181 real_file = FileSearch(path_element, name, ext);
183 if (real_file.empty()) {
185 tmppath = split(tmppath, path_element, ';');
186 } while(!tmppath.empty() && path_element.empty());
192 if (ext.empty() && notfound) {
193 real_file = FileOpenSearch(path, name, "exe");
194 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
201 /// Returns a vector of all files in directory dir having extension ext.
202 vector<string> const DirList(string const & dir, string const & ext)
204 // This is a non-error checking C/system implementation
205 string extension(ext);
206 if (!extension.empty() && extension[0] != '.')
207 extension.insert(0, ".");
208 vector<string> dirlist;
209 DIR * dirp = ::opendir(dir.c_str());
211 while ((dire = ::readdir(dirp))) {
212 string const fil = dire->d_name;
213 if (suffixIs(fil, extension)) {
214 dirlist.push_back(fil);
219 /* I would have prefered to take a vector<string>& as parameter so
220 that we could avoid the copy of the vector when returning.
222 dirlist.swap(argvec);
223 to avoid the copy. (Lgb)
225 /* A C++ implementaion will look like this:
226 string extension(ext);
227 if (extension[0] != '.') extension.insert(0, ".");
228 vector<string> dirlist;
229 directory_iterator dit("dir");
230 while (dit != directory_iterator()) {
231 string fil = dit->filename;
232 if (prefixIs(fil, extension)) {
233 dirlist.push_back(fil);
237 dirlist.swap(argvec);
243 // Returns the real name of file name in directory path, with optional
245 string const FileSearch(string const & path, string const & name,
248 // if `name' is an absolute path, we ignore the setting of `path'
249 // Expand Environmentvariables in 'name'
250 string const tmpname = ReplaceEnvironmentPath(name);
251 string fullname = MakeAbsPath(tmpname, path);
252 // search first without extension, then with it.
253 if (IsFileReadable(fullname))
255 else if (ext.empty())
257 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
260 if (IsFileReadable(fullname))
268 // Search the file name.ext in the subdirectory dir of
270 // 2) build_lyxdir (if not empty)
272 string const LibFileSearch(string const & dir, string const & name,
275 string fullname = FileSearch(AddPath(user_lyxdir, dir), name, ext);
276 if (!fullname.empty())
279 if (!build_lyxdir.empty())
280 fullname = FileSearch(AddPath(build_lyxdir, dir), name, ext);
281 if (!fullname.empty())
284 return FileSearch(AddPath(system_lyxdir, dir), name, ext);
289 i18nLibFileSearch(string const & dir, string const & name,
292 // this comment is from intl/dcigettext.c. We try to mimick this
294 /* The highest priority value is the `LANGUAGE' environment
295 variable. But we don't use the value if the currently
296 selected locale is the C locale. This is a GNU extension. */
298 string const lc_all = GetEnv("LC_ALL");
299 string lang = GetEnv("LANGUAGE");
300 if (lang.empty() || lc_all == "C") {
303 lang = GetEnv("LANG");
307 lang = token(lang, '_', 0);
309 if (lang.empty() || lang == "C")
310 return LibFileSearch(dir, name, ext);
312 string const tmp = LibFileSearch(dir, lang + '_' + name,
317 return LibFileSearch(dir, name, ext);
322 string const GetEnv(string const & envname)
324 // f.ex. what about error checking?
325 char const * const ch = getenv(envname.c_str());
326 string const envstr = !ch ? "" : ch;
331 string const GetEnvPath(string const & name)
334 string const pathlist = subst(GetEnv(name), ':', ';');
336 string const pathlist = os::slashify_path(GetEnv(name));
338 return strip(pathlist, ';');
342 bool PutEnv(string const & envstr)
344 // CHECK Look at and fix this.
345 // f.ex. what about error checking?
348 // this leaks, but what can we do about it?
349 // Is doing a getenv() and a free() of the older value
350 // a good idea? (JMarc)
351 // Actually we don't have to leak...calling putenv like this
352 // should be enough: ... and this is obviously not enough if putenv
353 // does not make a copy of the string. It is also not very wise to
354 // put a string on the free store. If we have to leak we should do it
356 char * leaker = new char[envstr.length() + 1];
357 envstr.copy(leaker, envstr.length());
358 leaker[envstr.length()] = '\0';
359 int const retval = lyx::putenv(leaker);
361 // If putenv does not make a copy of the char const * this
362 // is very dangerous. OTOH if it does take a copy this is the
364 // The only implementation of putenv that I have seen does not
365 // allocate memory. _And_ after testing the putenv in glibc it
366 // seems that we need to make a copy of the string contents.
367 // I will enable the above.
368 //int retval = lyx::putenv(envstr.c_str());
372 string const str = envstr.split(varname,'=');
373 int const retval = ::setenv(varname.c_str(), str.c_str(), true);
375 // No environment setting function. Can this happen?
376 int const retval = 1; //return an error condition.
383 bool PutEnvPath(string const & envstr)
385 return PutEnv(envstr);
391 int DeleteAllFilesInDir (string const & path)
393 // I have decided that we will be using parts from the boost
394 // library. Check out http://www.boost.org/
395 // For directory access we will then use the directory_iterator.
396 // Then the code will be something like:
397 // directory_iterator dit(path);
398 // directory_iterator dend;
399 // if (dit == dend) {
400 // WriteFSAlert(_("Error! Cannot open directory:"), path);
403 // for (; dit != dend; ++dit) {
404 // string filename(*dit);
405 // if (filename == "." || filename == "..")
407 // string unlinkpath(AddName(path, filename));
408 // if (lyx::unlink(unlinkpath))
409 // WriteFSAlert(_("Error! Could not remove file:"),
413 DIR * dir = ::opendir(path.c_str());
415 WriteFSAlert (_("Error! Cannot open directory:"), path);
419 int return_value = 0;
420 while ((de = readdir(dir))) {
421 string const temp = de->d_name;
422 if (temp == "." || temp == "..")
424 string const unlinkpath = AddName (path, temp);
426 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
430 if (FileInfo(unlinkpath).isDir())
431 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
432 deleted &= (lyx::unlink(unlinkpath) == 0);
434 WriteFSAlert (_("Error! Could not remove file:"),
444 string const CreateTmpDir(string const & tempdir, string const & mask)
447 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
448 << "CreateTmpDir: mask=`" << mask << "'" << endl;
450 string const tmpfl(lyx::tempName(tempdir, mask));
451 // lyx::tempName actually creates a file to make sure that it
452 // stays unique. So we have to delete it before we can create
453 // a dir with the same name. Note also that we are not thread
454 // safe because of the gap between unlink and mkdir. (Lgb)
455 lyx::unlink(tmpfl.c_str());
457 if (tmpfl.empty() || lyx::mkdir(tmpfl, 0700)) {
458 WriteFSAlert(_("Error! Couldn't create temporary directory:"),
462 return MakeAbsPath(tmpfl);
466 int DestroyTmpDir(string const & tmpdir, bool Allfiles)
471 if (Allfiles && DeleteAllFilesInDir(tmpdir)) {
474 if (lyx::rmdir(tmpdir)) {
475 WriteFSAlert(_("Error! Couldn't delete temporary directory:"),
485 string const CreateBufferTmpDir(string const & pathfor)
488 static string const tmpdir(pathfor.empty() ? os::getTmpDir() : pathfor);
489 // We are in our own directory. Why bother to mangle name?
490 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
492 string const tmpfl = tmpdir + "/lyx_tmpbuf" + tostr(count++);
493 if (lyx::mkdir(tmpfl, 0777)) {
494 WriteFSAlert(_("Error! Couldn't create temporary directory:"),
502 int DestroyBufferTmpDir(string const & tmpdir)
504 return DestroyTmpDir(tmpdir, true);
508 string const CreateLyXTmpDir(string const & deflt)
510 if ((!deflt.empty()) && (deflt != "/tmp")) {
511 if (lyx::mkdir(deflt, 0777)) {
515 return CreateTmpDir(deflt, "lyx_tmpdir");
522 return CreateTmpDir("/tmp", "lyx_tmpdir");
527 int DestroyLyXTmpDir(string const & tmpdir)
529 return DestroyTmpDir (tmpdir, false); // Why false?
533 // Creates directory. Returns true if succesfull
534 bool createDirectory(string const & path, int permission)
536 string temp(strip(os::slashify_path(path), '/'));
539 WriteAlert(_("Internal error!"),
540 _("Call to createDirectory with invalid name"));
544 if (lyx::mkdir(temp, permission)) {
545 WriteFSAlert (_("Error! Couldn't create directory:"), temp);
552 // Strip filename from path name
553 string const OnlyPath(string const & Filename)
555 // If empty filename, return empty
556 if (Filename.empty()) return Filename;
558 // Find last / or start of filename
559 string::size_type j = Filename.rfind('/');
560 if (j == string::npos)
562 return Filename.substr(0, j + 1);
566 // Convert relative path into absolute path based on a basepath.
567 // If relpath is absolute, just use that.
568 // If basepath is empty, use CWD as base.
569 string const MakeAbsPath(string const & RelPath, string const & BasePath)
571 // checks for already absolute path
572 if (os::is_absolute_path(RelPath))
575 // Copies given paths
576 string TempRel(os::slashify_path(RelPath));
577 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
578 TempRel = subst(TempRel, "//", "/");
582 if (os::is_absolute_path(BasePath))
585 TempBase = AddPath(lyx::getcwd(), BasePath);
587 // Handle /./ at the end of the path
588 while(suffixIs(TempBase, "/./"))
589 TempBase.erase(TempBase.length() - 2);
591 // processes relative path
592 string RTemp(TempRel);
595 while (!RTemp.empty()) {
597 RTemp = split(RTemp, Temp, '/');
599 if (Temp == ".") continue;
601 // Remove one level of TempBase
602 string::difference_type i = TempBase.length() - 2;
605 while (i > 0 && TempBase[i] != '/') --i;
609 while (i > 2 && TempBase[i] != '/') --i;
612 TempBase.erase(i, string::npos);
615 } else if (Temp.empty() && !RTemp.empty()) {
616 TempBase = os::current_root() + RTemp;
619 // Add this piece to TempBase
620 if (!suffixIs(TempBase, '/'))
626 // returns absolute path
627 return os::slashify_path(TempBase);
631 // Correctly append filename to the pathname.
632 // If pathname is '.', then don't use pathname.
633 // Chops any path of filename.
634 string const AddName(string const & path, string const & fname)
637 string const basename(OnlyFilename(fname));
641 if (path != "." && path != "./" && !path.empty()) {
642 buf = os::slashify_path(path);
643 if (!suffixIs(path, '/'))
647 return buf + basename;
651 // Strips path from filename
652 string const OnlyFilename(string const & fname)
657 string::size_type j = fname.rfind('/');
658 if (j == string::npos) // no '/' in fname
662 return fname.substr(j + 1);
666 /// Returns true is path is absolute
667 bool AbsolutePath(string const & path)
669 return os::is_absolute_path(path);
674 // Create absolute path. If impossible, don't do anything
675 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
676 string const ExpandPath(string const & path)
678 // checks for already absolute path
679 string RTemp(ReplaceEnvironmentPath(path));
680 if (os::is_absolute_path(RTemp))
684 string const copy(RTemp);
687 RTemp = split(RTemp, Temp, '/');
690 return lyx::getcwd() /*GetCWD()*/ + '/' + RTemp;
693 return GetEnvPath("HOME") + '/' + RTemp;
696 return MakeAbsPath(copy);
698 // Don't know how to handle this
704 // Constracts path/../path
705 // Can't handle "../../" or "/../" (Asger)
706 string const NormalizePath(string const & path)
712 if (os::is_absolute_path(path))
715 // Make implicit current directory explicit
718 while (!RTemp.empty()) {
720 RTemp = split(RTemp, Temp, '/');
724 } else if (Temp == "..") {
725 // Remove one level of TempBase
726 string::difference_type i = TempBase.length() - 2;
727 while (i > 0 && TempBase[i] != '/')
729 if (i >= 0 && TempBase[i] == '/')
730 TempBase.erase(i + 1, string::npos);
734 TempBase += Temp + '/';
738 // returns absolute path
743 string const GetFileContents(string const & fname)
745 FileInfo finfo(fname);
747 ifstream ifs(fname.c_str());
752 return ofs.str().c_str();
755 lyxerr << "LyX was not able to read file '" << fname << "'" << endl;
761 // Search ${...} as Variable-Name inside the string and replace it with
762 // the denoted environmentvariable
763 // Allow Variables according to
764 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
767 string const ReplaceEnvironmentPath(string const & path)
770 // CompareChar: Environmentvariables starts with this character
771 // PathChar: Next path component start with this character
772 // while CompareChar found do:
773 // Split String with PathChar
774 // Search Environmentvariable
775 // if found: Replace Strings
777 char const CompareChar = '$';
778 char const FirstChar = '{';
779 char const EndChar = '}';
780 char const UnderscoreChar = '_';
781 string EndString; EndString += EndChar;
782 string FirstString; FirstString += FirstChar;
783 string CompareString; CompareString += CompareChar;
784 string const RegExp("*}*"); // Exist EndChar inside a String?
786 // first: Search for a '$' - Sign.
788 string result1; //(copy); // for split-calls
789 string result0 = split(path, result1, CompareChar);
790 while (!result0.empty()) {
791 string copy1(result0); // contains String after $
793 // Check, if there is an EndChar inside original String.
795 if (!regexMatch(copy1, RegExp)) {
796 // No EndChar inside. So we are finished
797 result1 += CompareString + result0;
803 string res0 = split(copy1, res1, EndChar);
804 // Now res1 holds the environmentvariable
805 // First, check, if Contents is ok.
806 if (res1.empty()) { // No environmentvariable. Continue Loop.
807 result1 += CompareString + FirstString;
811 // check contents of res1
812 char const * res1_contents = res1.c_str();
813 if (*res1_contents != FirstChar) {
814 // Again No Environmentvariable
815 result1 += CompareString;
819 // Check for variable names
820 // Situation ${} is detected as "No Environmentvariable"
821 char const * cp1 = res1_contents + 1;
822 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
824 while (*cp1 && result) {
825 result = isalnum(*cp1) ||
826 (*cp1 == UnderscoreChar);
831 // no correct variable name
832 result1 += CompareString + res1 + EndString;
833 result0 = split(res0, res1, CompareChar);
838 string env(GetEnv(res1_contents + 1));
840 // Congratulations. Environmentvariable found
843 result1 += CompareString + res1 + EndString;
846 result0 = split(res0, res1, CompareChar);
850 } // ReplaceEnvironmentPath
853 // Make relative path out of two absolute paths
854 string const MakeRelPath(string const & abspath, string const & basepath)
855 // Makes relative path out of absolute path. If it is deeper than basepath,
856 // it's easy. If basepath and abspath share something (they are all deeper
857 // than some directory), it'll be rendered using ..'s. If they are completely
858 // different, then the absolute path will be used as relative path.
860 string::size_type const abslen = abspath.length();
861 string::size_type const baselen = basepath.length();
863 string::size_type i = os::common_path(abspath, basepath);
866 // actually no match - cannot make it relative
870 // Count how many dirs there are in basepath above match
871 // and append as many '..''s into relpath
873 string::size_type j = i;
874 while (j < baselen) {
875 if (basepath[j] == '/') {
876 if (j + 1 == baselen)
883 // Append relative stuff from common directory to abspath
884 if (abspath[i] == '/')
886 for (; i < abslen; ++i)
889 if (suffixIs(buf, '/'))
890 buf.erase(buf.length() - 1);
891 // Substitute empty with .
898 // Append sub-directory(ies) to a path in an intelligent way
899 string const AddPath(string const & path, string const & path_2)
902 string const path2 = os::slashify_path(path_2);
904 if (!path.empty() && path != "." && path != "./") {
905 buf = os::slashify_path(path);
906 if (path[path.length() - 1] != '/')
910 if (!path2.empty()) {
911 string::size_type const p2start = path2.find_first_not_of('/');
912 string::size_type const p2end = path2.find_last_not_of('/');
913 string const tmp = path2.substr(p2start, p2end - p2start + 1);
921 Change extension of oldname to extension.
922 Strips path off if no_path == true.
923 If no extension on oldname, just appends.
925 string const ChangeExtension(string const & oldname, string const & extension)
927 string::size_type const last_slash = oldname.rfind('/');
928 string::size_type last_dot = oldname.rfind('.');
929 if (last_dot < last_slash && last_slash != string::npos)
930 last_dot = string::npos;
933 // Make sure the extension starts with a dot
934 if (!extension.empty() && extension[0] != '.')
935 ext= "." + extension;
939 return os::slashify_path(oldname.substr(0, last_dot) + ext);
943 /// Return the extension of the file (not including the .)
944 string const GetExtension(string const & name)
946 string::size_type const last_slash = name.rfind('/');
947 string::size_type const last_dot = name.rfind('.');
948 if (last_dot != string::npos &&
949 (last_slash == string::npos || last_dot > last_slash))
950 return name.substr(last_dot + 1,
951 name.length() - (last_dot + 1));
957 // Creates a nice compact path for displaying
959 MakeDisplayPath (string const & path, unsigned int threshold)
961 string::size_type const l1 = path.length();
963 // First, we try a relative path compared to home
964 string const home(GetEnvPath("HOME"));
965 string relhome = MakeRelPath(path, home);
967 string::size_type l2 = relhome.length();
971 // If we backup from home or don't have a relative path,
972 // this try is no good
973 if (prefixIs(relhome, "../") || os::is_absolute_path(relhome)) {
974 // relative path was no good, just use the original path
981 // Is the path too long?
982 if (l2 > threshold) {
988 while (relhome.length() > threshold)
989 relhome = split(relhome, temp, '/');
991 // Did we shortend everything away?
992 if (relhome.empty()) {
993 // Yes, filename in itself is too long.
994 // Pick the start and the end of the filename.
995 relhome = OnlyFilename(path);
996 string const head = relhome.substr(0, threshold/2 - 3);
998 l2 = relhome.length();
1000 relhome.substr(l2 - threshold/2 - 2, l2 - 1);
1001 relhome = head + "..." + tail;
1004 return prefix + relhome;
1008 bool LyXReadLink(string const & File, string & Link)
1010 char LinkBuffer[512];
1011 // Should be PATH_MAX but that needs autconf support
1012 int const nRead = ::readlink(File.c_str(),
1013 LinkBuffer, sizeof(LinkBuffer) - 1);
1016 LinkBuffer[nRead] = '\0'; // terminator
1024 typedef pair<int, string> cmdret;
1026 cmdret const do_popen(string const & cmd)
1028 // One question is if we should use popen or
1029 // create our own popen based on fork, exec, pipe
1030 // of course the best would be to have a
1031 // pstream (process stream), with the
1032 // variants ipstream, opstream
1033 FILE * inf = ::popen(cmd.c_str(), "r");
1037 ret += static_cast<char>(c);
1040 int const pret = pclose(inf);
1041 return make_pair(pret, ret);
1047 string const findtexfile(string const & fil, string const & /*format*/)
1049 /* There is no problem to extend this function too use other
1050 methods to look for files. It could be setup to look
1051 in environment paths and also if wanted as a last resort
1052 to a recursive find. One of the easier extensions would
1053 perhaps be to use the LyX file lookup methods. But! I am
1054 going to implement this until I see some demand for it.
1058 // If the file can be found directly, we just return a
1059 // absolute path version of it.
1060 if (FileInfo(fil).exist())
1061 return MakeAbsPath(fil);
1063 // No we try to find it using kpsewhich.
1064 // It seems from the kpsewhich manual page that it is safe to use
1065 // kpsewhich without --format: "When the --format option is not
1066 // given, the search path used when looking for a file is inferred
1067 // from the name given, by looking for a known extension. If no
1068 // known extension is found, the search path for TeX source files
1070 // However, we want to take advantage of the format sine almost all
1071 // the different formats has environment variables that can be used
1072 // to controll which paths to search. f.ex. bib looks in
1073 // BIBINPUTS and TEXBIB. Small list follows:
1074 // bib - BIBINPUTS, TEXBIB
1076 // graphic/figure - TEXPICTS, TEXINPUTS
1077 // ist - TEXINDEXSTYLE, INDEXSTYLE
1078 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1080 // tfm - TFMFONTS, TEXFONTS
1081 // This means that to use kpsewhich in the best possible way we
1082 // should help it by setting additional path in the approp. envir.var.
1083 string const kpsecmd = "kpsewhich " + fil;
1085 cmdret const c = do_popen(kpsecmd);
1087 lyxerr[Debug::LATEX] << "kpse status = " << c.first << "\n"
1088 << "kpse result = `" << strip(c.second, '\n')
1091 return os::internal_path(strip(strip(c.second, '\n'), '\r'));
1097 void removeAutosaveFile(string const & filename)
1099 string a = OnlyPath(filename);
1101 a += OnlyFilename(filename);
1103 FileInfo const fileinfo(a);
1104 if (fileinfo.exist()) {
1105 if (lyx::unlink(a) != 0) {
1106 WriteFSAlert(_("Could not delete auto-save file!"), a);