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)
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>
63 extern string system_lyxdir;
64 extern string build_lyxdir;
65 extern string user_lyxdir;
66 extern string system_tempdir;
69 bool IsLyXFilename(string const & filename)
71 return suffixIs(lowercase(filename), ".lyx");
75 bool IsSGMLFilename(string const & filename)
77 return suffixIs(lowercase(filename), ".sgml");
81 // Substitutes spaces with underscores in filename (and path)
82 string const MakeLatexName(string const & file)
84 string name = OnlyFilename(file);
85 string const path = OnlyPath(file);
87 for (string::size_type i = 0; i < name.length(); ++i) {
88 name[i] &= 0x7f; // set 8th bit to 0
91 // ok so we scan through the string twice, but who cares.
92 string const keep("abcdefghijklmnopqrstuvwxyz"
93 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
94 "@!\"'()*+,-./0123456789:;<=>?[]`|");
96 string::size_type pos = 0;
97 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
100 return AddName(path, name);
104 // Substitutes spaces with underscores in filename (and path)
105 string const QuoteName(string const & name)
107 return (os::shell() == os::UNIX) ?
113 // Is a file readable ?
114 bool IsFileReadable (string const & path)
117 if (file.isOK() && file.isRegular() && file.readable())
124 // Is a file read_only?
125 // return 1 read-write
127 // -1 error (doesn't exist, no access, anything else)
128 int IsFileWriteable (string const & path)
131 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
133 if (fi.readable()) // read-only
135 return -1; // everything else.
139 //returns 1: dir writeable
141 // -1: error- couldn't find out
142 int IsDirWriteable (string const & path)
144 string const tmpfl(lyx::tempName(path, "lyxwritetest"));
145 // We must unlink the tmpfl.
149 WriteFSAlert(_("LyX Internal Error!"),
150 _("Could not test if directory is writeable"));
154 if (fi.writable()) return 1;
160 // Uses a string of paths separated by ";"s to find a file to open.
161 // Can't cope with pathnames with a ';' in them. Returns full path to file.
162 // If path entry begins with $$LyX/, use system_lyxdir
163 // If path entry begins with $$User/, use user_lyxdir
164 // Example: "$$User/doc;$$LyX/doc"
165 string const FileOpenSearch (string const & path, string const & name,
168 string real_file, path_element;
169 bool notfound = true;
170 string tmppath = split(path, path_element, ';');
172 while (notfound && !path_element.empty()) {
173 path_element = os::slashify_path(path_element);
174 if (!suffixIs(path_element, '/'))
176 path_element = subst(path_element, "$$LyX", system_lyxdir);
177 path_element = subst(path_element, "$$User", user_lyxdir);
179 real_file = FileSearch(path_element, name, ext);
181 if (real_file.empty()) {
183 tmppath = split(tmppath, path_element, ';');
184 } while(!tmppath.empty() && path_element.empty());
190 if (ext.empty() && notfound) {
191 real_file = FileOpenSearch(path, name, "exe");
192 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
199 /// Returns a vector of all files in directory dir having extension ext.
200 vector<string> const DirList(string const & dir, string const & ext)
202 // This is a non-error checking C/system implementation
203 string extension(ext);
204 if (!extension.empty() && extension[0] != '.')
205 extension.insert(0, ".");
206 vector<string> dirlist;
207 DIR * dirp = ::opendir(dir.c_str());
209 while ((dire = ::readdir(dirp))) {
210 string const fil = dire->d_name;
211 if (suffixIs(fil, extension)) {
212 dirlist.push_back(fil);
217 /* I would have prefered to take a vector<string>& as parameter so
218 that we could avoid the copy of the vector when returning.
220 dirlist.swap(argvec);
221 to avoid the copy. (Lgb)
223 /* A C++ implementaion will look like this:
224 string extension(ext);
225 if (extension[0] != '.') extension.insert(0, ".");
226 vector<string> dirlist;
227 directory_iterator dit("dir");
228 while (dit != directory_iterator()) {
229 string fil = dit->filename;
230 if (prefixIs(fil, extension)) {
231 dirlist.push_back(fil);
235 dirlist.swap(argvec);
241 // Returns the real name of file name in directory path, with optional
243 string const FileSearch(string const & path, string const & name,
246 // if `name' is an absolute path, we ignore the setting of `path'
247 // Expand Environmentvariables in 'name'
248 string const tmpname = ReplaceEnvironmentPath(name);
249 string fullname = MakeAbsPath(tmpname, path);
250 // search first without extension, then with it.
251 if (IsFileReadable(fullname))
253 else if (ext.empty())
255 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
258 if (IsFileReadable(fullname))
266 // Search the file name.ext in the subdirectory dir of
268 // 2) build_lyxdir (if not empty)
270 string const LibFileSearch(string const & dir, string const & name,
273 string fullname = FileSearch(AddPath(user_lyxdir, dir),
275 if (!fullname.empty())
278 if (!build_lyxdir.empty())
279 fullname = FileSearch(AddPath(build_lyxdir, dir),
281 if (!fullname.empty())
284 return FileSearch(AddPath(system_lyxdir, dir), name, ext);
289 i18nLibFileSearch(string const & dir, string const & name,
292 string const lang = token(string(GetEnv("LANG")), '_', 0);
294 if (lang.empty() || lang == "C")
295 return LibFileSearch(dir, name, ext);
297 string const tmp = LibFileSearch(dir, lang + '_' + name,
302 return LibFileSearch(dir, name, ext);
307 string const GetEnv(string const & envname)
309 // f.ex. what about error checking?
310 char const * const ch = getenv(envname.c_str());
311 string const envstr = !ch ? "" : ch;
316 string const GetEnvPath(string const & name)
319 string const pathlist = subst(GetEnv(name), ':', ';');
321 string const pathlist = os::slashify_path(GetEnv(name));
323 return strip(pathlist, ';');
327 bool PutEnv(string const & envstr)
329 // CHECK Look at and fix this.
330 // f.ex. what about error checking?
333 // this leaks, but what can we do about it?
334 // Is doing a getenv() and a free() of the older value
335 // a good idea? (JMarc)
336 // Actually we don't have to leak...calling putenv like this
337 // should be enough: ... and this is obviously not enough if putenv
338 // does not make a copy of the string. It is also not very wise to
339 // put a string on the free store. If we have to leak we should do it
341 char * leaker = new char[envstr.length() + 1];
342 envstr.copy(leaker, envstr.length());
343 leaker[envstr.length()] = '\0';
344 int const retval = lyx::putenv(leaker);
346 // If putenv does not make a copy of the char const * this
347 // is very dangerous. OTOH if it does take a copy this is the
349 // The only implementation of putenv that I have seen does not
350 // allocate memory. _And_ after testing the putenv in glibc it
351 // seems that we need to make a copy of the string contents.
352 // I will enable the above.
353 //int retval = lyx::putenv(envstr.c_str());
357 string const str = envstr.split(varname,'=');
358 int const retval = ::setenv(varname.c_str(), str.c_str(), true);
360 // No environment setting function. Can this happen?
361 int const retval = 1; //return an error condition.
368 bool PutEnvPath(string const & envstr)
370 return PutEnv(envstr);
376 int DeleteAllFilesInDir (string const & path)
378 // I have decided that we will be using parts from the boost
379 // library. Check out http://www.boost.org/
380 // For directory access we will then use the directory_iterator.
381 // Then the code will be something like:
382 // directory_iterator dit(path);
383 // directory_iterator dend;
384 // if (dit == dend) {
385 // WriteFSAlert(_("Error! Cannot open directory:"), path);
388 // for (; dit != dend; ++dit) {
389 // string filename(*dit);
390 // if (filename == "." || filename == "..")
392 // string unlinkpath(AddName(path, filename));
393 // if (lyx::unlink(unlinkpath))
394 // WriteFSAlert(_("Error! Could not remove file:"),
398 DIR * dir = ::opendir(path.c_str());
400 WriteFSAlert (_("Error! Cannot open directory:"), path);
404 int return_value = 0;
405 while ((de = readdir(dir))) {
406 string const temp = de->d_name;
407 if (temp == "." || temp == "..")
409 string const unlinkpath = AddName (path, temp);
411 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
415 if (FileInfo(unlinkpath).isDir())
416 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
417 deleted &= (lyx::unlink(unlinkpath) == 0);
419 WriteFSAlert (_("Error! Could not remove file:"),
429 string const CreateTmpDir(string const & tempdir, string const & mask)
432 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
433 << "CreateTmpDir: mask=`" << mask << "'" << endl;
435 string const tmpfl(lyx::tempName(tempdir, mask));
436 // lyx::tempName actually creates a file to make sure that it
437 // stays unique. So we have to delete it before we can create
438 // a dir with the same name. Note also that we are not thread
439 // safe because of the gap between unlink and mkdir. (Lgb)
440 lyx::unlink(tmpfl.c_str());
442 if (tmpfl.empty() || lyx::mkdir(tmpfl, 0700)) {
443 WriteFSAlert(_("Error! Couldn't create temporary directory:"),
447 return MakeAbsPath(tmpfl);
451 int DestroyTmpDir(string const & tmpdir, bool Allfiles)
456 if (Allfiles && DeleteAllFilesInDir(tmpdir)) return -1;
457 if (lyx::rmdir(tmpdir)) {
458 WriteFSAlert(_("Error! Couldn't delete temporary directory:"),
468 string const CreateBufferTmpDir(string const & pathfor)
471 static string const tmpdir(pathfor.empty() ? os::getTmpDir() : pathfor);
472 // We are in our own directory. Why bother to mangle name?
473 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
475 string const tmpfl = tmpdir + "/lyx_tmpbuf" + tostr(count++);
476 if (lyx::mkdir(tmpfl, 0777)) {
477 WriteFSAlert(_("Error! Couldn't create temporary directory:"),
485 int DestroyBufferTmpDir(string const & tmpdir)
487 return DestroyTmpDir(tmpdir, true);
491 string const CreateLyXTmpDir(string const & deflt)
493 if ((!deflt.empty()) && (deflt != "/tmp")) {
494 if (lyx::mkdir(deflt, 0777)) {
498 string const t(CreateTmpDir(deflt, "lyx_tmpdir"));
506 string const t(CreateTmpDir("/tmp", "lyx_tmpdir"));
512 int DestroyLyXTmpDir(string const & tmpdir)
514 return DestroyTmpDir (tmpdir, false); // Why false?
518 // Creates directory. Returns true if succesfull
519 bool createDirectory(string const & path, int permission)
521 string temp(strip(os::slashify_path(path), '/'));
524 WriteAlert(_("Internal error!"),
525 _("Call to createDirectory with invalid name"));
529 if (lyx::mkdir(temp, permission)) {
530 WriteFSAlert (_("Error! Couldn't create directory:"), temp);
537 // Strip filename from path name
538 string const OnlyPath(string const & Filename)
540 // If empty filename, return empty
541 if (Filename.empty()) return Filename;
543 // Find last / or start of filename
544 string::size_type j = Filename.rfind('/');
545 if (j == string::npos)
547 return Filename.substr(0, j + 1);
551 // Convert relative path into absolute path based on a basepath.
552 // If relpath is absolute, just use that.
553 // If basepath is empty, use CWD as base.
554 string const MakeAbsPath(string const & RelPath, string const & BasePath)
556 // checks for already absolute path
557 if (AbsolutePath(RelPath))
560 // Copies given paths
561 string TempRel(os::slashify_path(RelPath));
562 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
563 TempRel = subst(TempRel, "//", "/");
567 if (AbsolutePath(BasePath))
570 TempBase = AddPath(lyx::getcwd(), BasePath);
572 // Handle /./ at the end of the path
573 while(suffixIs(TempBase, "/./"))
574 TempBase.erase(TempBase.length() - 2);
576 // processes relative path
577 string RTemp(TempRel);
580 while (!RTemp.empty()) {
582 RTemp = split(RTemp, Temp, '/');
584 if (Temp == ".") continue;
586 // Remove one level of TempBase
587 string::difference_type i = TempBase.length() - 2;
590 while (i > 0 && TempBase[i] != '/') --i;
594 while (i > 2 && TempBase[i] != '/') --i;
597 TempBase.erase(i, string::npos);
600 } else if (Temp.empty() && !RTemp.empty()) {
601 TempBase = os::current_root() + RTemp;
604 // Add this piece to TempBase
605 if (!suffixIs(TempBase, '/'))
611 // returns absolute path
612 return os::slashify_path(TempBase);
616 // Correctly append filename to the pathname.
617 // If pathname is '.', then don't use pathname.
618 // Chops any path of filename.
619 string const AddName(string const & path, string const & fname)
622 string const basename(OnlyFilename(fname));
626 if (path != "." && path != "./" && !path.empty()) {
627 buf = os::slashify_path(path);
628 if (!suffixIs(path, '/'))
632 return buf + basename;
636 // Strips path from filename
637 string const OnlyFilename(string const & fname)
642 string::size_type j = fname.rfind('/');
643 if (j == string::npos) // no '/' in fname
647 return fname.substr(j + 1);
651 // Is a filename/path absolute?
652 bool AbsolutePath(string const & path)
655 return (!path.empty() && path[0] == '/');
657 return (!path.empty() && isalpha(static_cast<unsigned char>(path[0])) && path.length() > 1 && path[1] == ':');
662 // Create absolute path. If impossible, don't do anything
663 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
664 string const ExpandPath(string const & path)
666 // checks for already absolute path
667 string RTemp(ReplaceEnvironmentPath(path));
668 if (AbsolutePath(RTemp))
672 string const copy(RTemp);
675 RTemp = split(RTemp, Temp, '/');
678 return lyx::getcwd() /*GetCWD()*/ + '/' + RTemp;
679 } else if (Temp == "~") {
680 return GetEnvPath("HOME") + '/' + RTemp;
681 } else if (Temp == "..") {
682 return MakeAbsPath(copy);
684 // Don't know how to handle this
690 // Constracts path/../path
691 // Can't handle "../../" or "/../" (Asger)
692 string const NormalizePath(string const & path)
698 if (AbsolutePath(path))
701 // Make implicit current directory explicit
704 while (!RTemp.empty()) {
706 RTemp = split(RTemp, Temp, '/');
710 } else if (Temp == "..") {
711 // Remove one level of TempBase
712 string::difference_type i = TempBase.length() - 2;
713 while (i > 0 && TempBase[i] != '/')
715 if (i >= 0 && TempBase[i] == '/')
716 TempBase.erase(i + 1, string::npos);
720 TempBase += Temp + '/';
724 // returns absolute path
729 string const GetFileContents(string const & fname)
731 FileInfo finfo(fname);
733 ifstream ifs(fname.c_str());
734 std::ostringstream ofs;
738 return ofs.str().c_str();
741 lyxerr << "LyX was not able to read file '" << fname << "'" << endl;
747 // Search ${...} as Variable-Name inside the string and replace it with
748 // the denoted environmentvariable
749 // Allow Variables according to
750 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
753 string const ReplaceEnvironmentPath(string const & path)
756 // CompareChar: Environmentvariables starts with this character
757 // PathChar: Next path component start with this character
758 // while CompareChar found do:
759 // Split String with PathChar
760 // Search Environmentvariable
761 // if found: Replace Strings
763 char const CompareChar = '$';
764 char const FirstChar = '{';
765 char const EndChar = '}';
766 char const UnderscoreChar = '_';
767 string EndString; EndString += EndChar;
768 string FirstString; FirstString += FirstChar;
769 string CompareString; CompareString += CompareChar;
770 string const RegExp("*}*"); // Exist EndChar inside a String?
772 // first: Search for a '$' - Sign.
774 string result1; //(copy); // for split-calls
775 string result0 = split(path, result1, CompareChar);
776 while (!result0.empty()) {
777 string copy1(result0); // contains String after $
779 // Check, if there is an EndChar inside original String.
781 if (!regexMatch(copy1, RegExp)) {
782 // No EndChar inside. So we are finished
783 result1 += CompareString + result0;
789 string res0 = split(copy1, res1, EndChar);
790 // Now res1 holds the environmentvariable
791 // First, check, if Contents is ok.
792 if (res1.empty()) { // No environmentvariable. Continue Loop.
793 result1 += CompareString + FirstString;
797 // check contents of res1
798 char const * res1_contents = res1.c_str();
799 if (*res1_contents != FirstChar) {
800 // Again No Environmentvariable
801 result1 += CompareString;
805 // Check for variable names
806 // Situation ${} is detected as "No Environmentvariable"
807 char const * cp1 = res1_contents + 1;
808 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
810 while (*cp1 && result) {
811 result = isalnum(*cp1) ||
812 (*cp1 == UnderscoreChar);
817 // no correct variable name
818 result1 += CompareString + res1 + EndString;
819 result0 = split(res0, res1, CompareChar);
824 string env(GetEnv(res1_contents + 1));
826 // Congratulations. Environmentvariable found
829 result1 += CompareString + res1 + EndString;
832 result0 = split(res0, res1, CompareChar);
836 } // ReplaceEnvironmentPath
839 // Make relative path out of two absolute paths
840 string const MakeRelPath(string const & abspath, string const & basepath)
841 // Makes relative path out of absolute path. If it is deeper than basepath,
842 // it's easy. If basepath and abspath share something (they are all deeper
843 // than some directory), it'll be rendered using ..'s. If they are completely
844 // different, then the absolute path will be used as relative path.
846 string::size_type const abslen = abspath.length();
847 string::size_type const baselen = basepath.length();
849 string::size_type i = os::common_path(abspath, basepath);
852 // actually no match - cannot make it relative
856 // Count how many dirs there are in basepath above match
857 // and append as many '..''s into relpath
859 string::size_type j = i;
860 while (j < baselen) {
861 if (basepath[j] == '/') {
862 if (j + 1 == baselen) break;
868 // Append relative stuff from common directory to abspath
869 if (abspath[i] == '/') ++i;
870 for (; i < abslen; ++i)
873 if (suffixIs(buf, '/'))
874 buf.erase(buf.length() - 1);
875 // Substitute empty with .
882 // Append sub-directory(ies) to a path in an intelligent way
883 string const AddPath(string const & path, string const & path_2)
886 string const path2 = os::slashify_path(path_2);
888 if (!path.empty() && path != "." && path != "./") {
889 buf = os::slashify_path(path);
890 if (path[path.length() - 1] != '/')
894 if (!path2.empty()) {
895 string::size_type const p2start = path2.find_first_not_of('/');
896 string::size_type const p2end = path2.find_last_not_of('/');
897 string const tmp = path2.substr(p2start, p2end - p2start + 1);
905 Change extension of oldname to extension.
906 Strips path off if no_path == true.
907 If no extension on oldname, just appends.
910 ChangeExtension(string const & oldname, string const & extension)
912 string::size_type const last_slash = oldname.rfind('/');
913 string::size_type last_dot = oldname.rfind('.');
914 if (last_dot < last_slash && last_slash != string::npos)
915 last_dot = string::npos;
918 // Make sure the extension starts with a dot
919 if (!extension.empty() && extension[0] != '.')
920 ext= "." + extension;
924 return os::slashify_path(oldname.substr(0, last_dot) + ext);
928 /// Return the extension of the file (not including the .)
929 string const GetExtension(string const & name)
931 string::size_type const last_slash = name.rfind('/');
932 string::size_type const last_dot = name.rfind('.');
933 if (last_dot != string::npos &&
934 (last_slash == string::npos || last_dot > last_slash))
935 return name.substr(last_dot + 1,
936 name.length() - (last_dot + 1));
942 // Creates a nice compact path for displaying
944 MakeDisplayPath (string const & path, unsigned int threshold)
946 string::size_type const l1 = path.length();
948 // First, we try a relative path compared to home
949 string const home(GetEnvPath("HOME"));
950 string relhome = MakeRelPath(path, home);
952 string::size_type l2 = relhome.length();
956 // If we backup from home or don't have a relative path,
957 // this try is no good
958 if (prefixIs(relhome, "../") || AbsolutePath(relhome)) {
959 // relative path was no good, just use the original path
966 // Is the path too long?
967 if (l2 > threshold) {
973 while (relhome.length() > threshold)
974 relhome = split(relhome, temp, '/');
976 // Did we shortend everything away?
977 if (relhome.empty()) {
978 // Yes, filename in itself is too long.
979 // Pick the start and the end of the filename.
980 relhome = OnlyFilename(path);
981 string const head = relhome.substr(0, threshold/2 - 3);
983 l2 = relhome.length();
985 relhome.substr(l2 - threshold/2 - 2, l2 - 1);
986 relhome = head + "..." + tail;
989 return prefix + relhome;
993 bool LyXReadLink(string const & File, string & Link)
995 char LinkBuffer[512];
996 // Should be PATH_MAX but that needs autconf support
997 int const nRead = ::readlink(File.c_str(),
998 LinkBuffer, sizeof(LinkBuffer) - 1);
1001 LinkBuffer[nRead] = '\0'; // terminator
1009 typedef pair<int, string> cmdret;
1011 cmdret const do_popen(string const & cmd)
1013 // One question is if we should use popen or
1014 // create our own popen based on fork, exec, pipe
1015 // of course the best would be to have a
1016 // pstream (process stream), with the
1017 // variants ipstream, opstream
1018 FILE * inf = ::popen(cmd.c_str(), "r");
1022 ret += static_cast<char>(c);
1025 int const pret = pclose(inf);
1026 return make_pair(pret, ret);
1033 findtexfile(string const & fil, string const & /*format*/)
1035 /* There is no problem to extend this function too use other
1036 methods to look for files. It could be setup to look
1037 in environment paths and also if wanted as a last resort
1038 to a recursive find. One of the easier extensions would
1039 perhaps be to use the LyX file lookup methods. But! I am
1040 going to implement this until I see some demand for it.
1044 // If the file can be found directly, we just return a
1045 // absolute path version of it.
1046 if (FileInfo(fil).exist())
1047 return MakeAbsPath(fil);
1049 // No we try to find it using kpsewhich.
1050 // It seems from the kpsewhich manual page that it is safe to use
1051 // kpsewhich without --format: "When the --format option is not
1052 // given, the search path used when looking for a file is inferred
1053 // from the name given, by looking for a known extension. If no
1054 // known extension is found, the search path for TeX source files
1056 // However, we want to take advantage of the format sine almost all
1057 // the different formats has environment variables that can be used
1058 // to controll which paths to search. f.ex. bib looks in
1059 // BIBINPUTS and TEXBIB. Small list follows:
1060 // bib - BIBINPUTS, TEXBIB
1062 // graphic/figure - TEXPICTS, TEXINPUTS
1063 // ist - TEXINDEXSTYLE, INDEXSTYLE
1064 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1066 // tfm - TFMFONTS, TEXFONTS
1067 // This means that to use kpsewhich in the best possible way we
1068 // should help it by setting additional path in the approp. envir.var.
1069 string const kpsecmd = "kpsewhich " + fil;
1071 cmdret const c = do_popen(kpsecmd);
1073 lyxerr[Debug::LATEX] << "kpse status = " << c.first << "\n"
1074 << "kpse result = `" << strip(c.second, '\n')
1077 return strip(strip(c.second, '\n'), '\r');
1083 void removeAutosaveFile(string const & filename)
1085 string a = OnlyPath(filename);
1087 a += OnlyFilename(filename);
1089 FileInfo const fileinfo(a);
1090 if (fileinfo.exist()) {
1091 if (lyx::unlink(a) != 0) {
1092 WriteFSAlert(_("Could not delete auto-save file!"), a);