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)
38 // Which part of this is still necessary? (JMarc).
41 # define NAMLEN(dirent) strlen((dirent)->d_name)
43 # define dirent direct
44 # define NAMLEN(dirent) (dirent)->d_namlen
46 # include <sys/ndir.h>
68 extern string system_lyxdir;
69 extern string build_lyxdir;
70 extern string user_lyxdir;
71 extern string system_tempdir;
74 bool IsLyXFilename(string const & filename)
76 return contains(filename, ".lyx");
80 // Substitutes spaces with underscores in filename (and path)
81 string const MakeLatexName(string const & file)
83 string name = OnlyFilename(file);
84 string const path = OnlyPath(file);
86 for (string::size_type i = 0; i < name.length(); ++i) {
87 name[i] &= 0x7f; // set 8th bit to 0
90 // ok so we scan through the string twice, but who cares.
91 string const keep("abcdefghijklmnopqrstuvwxyz"
92 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
93 "@!\"'()*+,-./0123456789:;<=>?[]`|");
95 string::size_type pos = 0;
96 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
99 return AddName(path, name);
103 // Substitutes spaces with underscores in filename (and path)
104 string const QuoteName(string const & name)
106 // CHECK Add proper emx support here!
108 return "\'" + name + "\'";
115 // Returns an unique name to be used as a temporary file.
116 string const TmpFileName(string const & dir, string const & mask)
117 {// With all these temporary variables, it should be safe enough :-) (JMarc)
120 tmpdir = system_tempdir;
123 string tmpfl(AddName(tmpdir, mask));
125 // find a uniq postfix for the filename...
126 // using the pid, and...
127 tmpfl += tostr(getpid());
131 for (int a = 'a'; a <= 'z'; ++a)
132 for (int b = 'a'; b <= 'z'; ++b)
133 for (int c = 'a'; c <= 'z'; ++c) {
134 // if this is not enough I have no idea what
136 ret = tmpfl + char(a) + char(b) + char(c);
137 // check if the file exist
138 if (!fnfo.newFile(ret).exist())
141 lyxerr << "Not able to find a uniq tmpfile name." << endl;
146 // Is a file readable ?
147 bool IsFileReadable (string const & path)
150 if (file.isOK() && file.isRegular() && file.readable())
157 // Is a file read_only?
158 // return 1 read-write
160 // -1 error (doesn't exist, no access, anything else)
161 int IsFileWriteable (string const & path)
164 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
166 if (fi.readable()) // read-only
168 return -1; // everything else.
172 //returns 1: dir writeable
174 // -1: error- couldn't find out
175 int IsDirWriteable (string const & path)
177 string const tmpfl(TmpFileName(path));
180 WriteFSAlert(_("LyX Internal Error!"),
181 _("Could not test if directory is writeable"));
185 if (fi.writable()) return 1;
191 // Uses a string of paths separated by ";"s to find a file to open.
192 // Can't cope with pathnames with a ';' in them. Returns full path to file.
193 // If path entry begins with $$LyX/, use system_lyxdir
194 // If path entry begins with $$User/, use user_lyxdir
195 // Example: "$$User/doc;$$LyX/doc"
196 string const FileOpenSearch (string const & path, string const & name,
199 string real_file, path_element;
200 bool notfound = true;
201 string tmppath = split(path, path_element, ';');
203 while (notfound && !path_element.empty()) {
204 path_element = CleanupPath(path_element);
205 if (!suffixIs(path_element, '/'))
207 path_element = subst(path_element, "$$LyX", system_lyxdir);
208 path_element = subst(path_element, "$$User", user_lyxdir);
210 real_file = FileSearch(path_element, name, ext);
212 if (real_file.empty()) {
214 tmppath = split(tmppath, path_element, ';');
215 } while(!tmppath.empty() && path_element.empty());
221 if (ext.empty() && notfound) {
222 real_file = FileOpenSearch(path, name, "exe");
223 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
230 // Returns the real name of file name in directory path, with optional
232 string const FileSearch(string const & path, string const & name,
235 // if `name' is an absolute path, we ignore the setting of `path'
236 // Expand Environmentvariables in 'name'
237 string const tmpname = ReplaceEnvironmentPath(name);
238 string fullname = MakeAbsPath(tmpname, path);
240 // search first without extension, then with it.
241 if (IsFileReadable(fullname))
243 else if (ext.empty())
245 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
248 if (IsFileReadable(fullname))
256 // Search the file name.ext in the subdirectory dir of
258 // 2) build_lyxdir (if not empty)
260 string const LibFileSearch(string const & dir, string const & name,
263 string fullname = FileSearch(AddPath(user_lyxdir, dir),
265 if (!fullname.empty())
268 if (!build_lyxdir.empty())
269 fullname = FileSearch(AddPath(build_lyxdir, dir),
271 if (!fullname.empty())
274 return FileSearch(AddPath(system_lyxdir, dir), name, ext);
279 i18nLibFileSearch(string const & dir, string const & name,
282 string const lang = token(string(GetEnv("LANG")), '_', 0);
284 if (lang.empty() || lang == "C")
285 return LibFileSearch(dir, name, ext);
287 string const tmp = LibFileSearch(dir, lang + '_' + name,
292 return LibFileSearch(dir, name, ext);
297 string const GetEnv(string const & envname)
299 // f.ex. what about error checking?
300 char const * const ch = getenv(envname.c_str());
301 string const envstr = !ch ? "" : ch;
306 string const GetEnvPath(string const & name)
309 string const pathlist = subst(GetEnv(name), ':', ';');
311 string const pathlist = subst(GetEnv(name), '\\', '/');
313 return strip(pathlist, ';');
317 bool PutEnv(string const & envstr)
319 // CHECK Look at and fix this.
320 // f.ex. what about error checking?
323 // this leaks, but what can we do about it?
324 // Is doing a getenv() and a free() of the older value
325 // a good idea? (JMarc)
326 // Actually we don't have to leak...calling putenv like this
327 // should be enough: ... and this is obviously not enough if putenv
328 // does not make a copy of the string. It is also not very wise to
329 // put a string on the free store. If we have to leak we should do it
331 char * leaker = new char[envstr.length() + 1];
332 envstr.copy(leaker, envstr.length());
333 leaker[envstr.length()] = '\0';
334 int const retval = lyx::putenv(leaker);
336 // If putenv does not make a copy of the char const * this
337 // is very dangerous. OTOH if it does take a copy this is the
339 // The only implementation of putenv that I have seen does not
340 // allocate memory. _And_ after testing the putenv in glibc it
341 // seems that we need to make a copy of the string contents.
342 // I will enable the above.
343 //int retval = lyx::putenv(envstr.c_str());
347 string const str = envstr.split(varname,'=');
348 int const retval = ::setenv(varname.c_str(), str.c_str(), true);
350 // No environment setting function. Can this happen?
351 int const retval = 1; //return an error condition.
358 bool PutEnvPath(string const & envstr)
360 return PutEnv(envstr);
365 int DeleteAllFilesInDir (string const & path)
367 // I have decided that we will be using parts from the boost
368 // library. Check out http://www.boost.org/
369 // For directory access we will then use the directory_iterator.
370 // Then the code will be something like:
371 // directory_iterator dit(path);
372 // directory_iterator dend;
373 // if (dit == dend) {
374 // WriteFSAlert(_("Error! Cannot open directory:"), path);
377 // for (; dit != dend; ++dit) {
378 // string filename(*dit);
379 // if (filename == "." || filename == "..")
381 // string unlinkpath(AddName(path, filename));
382 // if (lyx::unlink(unlinkpath))
383 // WriteFSAlert(_("Error! Could not remove file:"),
387 DIR * dir = ::opendir(path.c_str());
389 WriteFSAlert (_("Error! Cannot open directory:"), path);
393 int return_value = 0;
394 while ((de = readdir(dir))) {
395 string const temp = de->d_name;
396 if (temp == "." || temp == "..")
398 string const unlinkpath = AddName (path, temp);
400 lyxerr.debug() << "Deleting file: " << unlinkpath << endl;
403 if (FileInfo(unlinkpath).isDir())
404 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
405 deleted &= (lyx::unlink(unlinkpath) == 0);
407 WriteFSAlert (_("Error! Could not remove file:"),
418 string const CreateTmpDir(string const & tempdir, string const & mask)
420 string const tmpfl(TmpFileName(tempdir, mask));
422 if ((tmpfl.empty()) || lyx::mkdir(tmpfl, 0777)) {
423 WriteFSAlert(_("Error! Couldn't create temporary directory:"),
427 return MakeAbsPath(tmpfl);
432 int DestroyTmpDir(string const & tmpdir, bool Allfiles)
437 if (Allfiles && DeleteAllFilesInDir(tmpdir)) return -1;
438 if (lyx::rmdir(tmpdir)) {
439 WriteFSAlert(_("Error! Couldn't delete temporary directory:"),
447 string const CreateBufferTmpDir(string const & pathfor)
449 return CreateTmpDir(pathfor, "lyx_bufrtmp");
453 int DestroyBufferTmpDir(string const & tmpdir)
455 return DestroyTmpDir(tmpdir, true);
459 string const CreateLyXTmpDir(string const & deflt)
461 if ((!deflt.empty()) && (deflt != "/tmp")) {
462 if (lyx::mkdir(deflt, 0777)) {
466 string const t(CreateTmpDir(deflt, "lyx_tmp"));
474 string const t(CreateTmpDir("/tmp", "lyx_tmp"));
480 int DestroyLyXTmpDir (string const & tmpdir)
482 return DestroyTmpDir (tmpdir, false); // Why false?
486 // Creates directory. Returns true if succesfull
487 bool createDirectory(string const & path, int permission)
489 string temp(strip(CleanupPath(path), '/'));
492 WriteAlert(_("Internal error!"),
493 _("Call to createDirectory with invalid name"));
497 if (lyx::mkdir(temp, permission)) {
498 WriteFSAlert (_("Error! Couldn't create directory:"), temp);
505 // Returns current working directory
506 string const GetCWD ()
508 int n = 256; // Assume path is less than 256 chars
510 char * tbuf = new char[n];
512 // Safe. Hopefully all getcwds behave this way!
513 while (((err = lyx::getcwd(tbuf, n)) == 0) && (errno == ERANGE)) {
514 // Buffer too small, double the buffersize and try again
521 if (err) result = tbuf;
527 // Strip filename from path name
528 string const OnlyPath(string const & Filename)
530 // If empty filename, return empty
531 if (Filename.empty()) return Filename;
533 // Find last / or start of filename
534 string::size_type j = Filename.rfind('/');
535 if (j == string::npos)
537 return Filename.substr(0, j + 1);
541 // Convert relative path into absolute path based on a basepath.
542 // If relpath is absolute, just use that.
543 // If basepath is empty, use CWD as base.
544 string const MakeAbsPath(string const & RelPath, string const & BasePath)
546 // checks for already absolute path
547 if (AbsolutePath(RelPath))
549 if (RelPath[0]!= '/' && RelPath[0]!= '\\')
553 // Copies given paths
554 string TempRel(CleanupPath(RelPath));
558 if (!BasePath.empty()) {
562 char * with_drive = new char[_MAX_PATH];
563 _abspath(with_drive, BasePath.c_str(), _MAX_PATH);
564 TempBase = with_drive;
570 if (AbsolutePath(TempRel))
571 return TempBase.substr(0, 2) + TempRel;
574 // Handle /./ at the end of the path
575 while(suffixIs(TempBase, "/./"))
576 TempBase.erase(TempBase.length() - 2);
578 // processes relative path
579 string RTemp(TempRel);
582 while (!RTemp.empty()) {
584 RTemp = split(RTemp, Temp, '/');
586 if (Temp == ".") continue;
588 // Remove one level of TempBase
589 string::difference_type i = TempBase.length() - 2;
592 while (i > 0 && TempBase[i] != '/') --i;
596 while (i > 2 && TempBase[i] != '/') --i;
599 TempBase.erase(i, string::npos);
603 // Add this piece to TempBase
604 if (!suffixIs(TempBase, '/'))
610 // returns absolute path
615 // Correctly append filename to the pathname.
616 // If pathname is '.', then don't use pathname.
617 // Chops any path of filename.
618 string const AddName(string const & path, string const & fname)
621 string const basename(OnlyFilename(fname));
625 if (path != "." && path != "./" && !path.empty()) {
626 buf = CleanupPath(path);
627 if (!suffixIs(path, '/'))
631 return buf + basename;
635 // Strips path from filename
636 string const OnlyFilename(string const & fname)
641 string::size_type j = fname.rfind('/');
642 if (j == string::npos) // no '/' in fname
646 return fname.substr(j + 1);
650 // Is a filename/path absolute?
651 bool AbsolutePath(string const & path)
654 return (!path.empty() && path[0] == '/');
656 return (!path.empty() && (path[0] == '/' || (isalpha(static_cast<unsigned char>(path[0])) && path.length()>1 && path[1] == ':')));
661 // Create absolute path. If impossible, don't do anything
662 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
663 string const ExpandPath(string const & path)
665 // checks for already absolute path
666 string RTemp(ReplaceEnvironmentPath(path));
667 if (AbsolutePath(RTemp))
671 string const copy(RTemp);
674 RTemp = split(RTemp, Temp, '/');
677 return GetCWD() + '/' + RTemp;
678 } else if (Temp == "~") {
679 return GetEnvPath("HOME") + '/' + RTemp;
680 } else if (Temp == "..") {
681 return MakeAbsPath(copy);
683 // Don't know how to handle this
689 // Constracts path/../path
690 // Can't handle "../../" or "/../" (Asger)
691 string const NormalizePath(string const & path)
697 if (AbsolutePath(path))
700 // Make implicit current directory explicit
703 while (!RTemp.empty()) {
705 RTemp = split(RTemp, Temp, '/');
709 } else if (Temp == "..") {
710 // Remove one level of TempBase
711 string::difference_type i = TempBase.length() - 2;
712 while (i > 0 && TempBase[i] != '/')
714 if (i >= 0 && TempBase[i] == '/')
715 TempBase.erase(i + 1, string::npos);
719 TempBase += Temp + '/';
723 // returns absolute path
728 string const CleanupPath(string const & path)
730 #ifdef __EMX__ /* SMiyata: This should fix searchpath bug. */
731 string temppath = subst(path, '\\', '/');
732 temppath = subst(temppath, "//", "/");
733 return lowercase(temppath);
734 #else // On unix, nothing to do
740 string const GetFileContents(string const & fname)
742 FileInfo finfo(fname);
744 ifstream ifs(fname.c_str());
745 std::ostringstream ofs;
752 lyxerr << "LyX was not able to read file '" << fname << "'" << endl;
758 // Search ${...} as Variable-Name inside the string and replace it with
759 // the denoted environmentvariable
760 // Allow Variables according to
761 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
764 string const ReplaceEnvironmentPath(string const & path)
767 // CompareChar: Environmentvariables starts with this character
768 // PathChar: Next path component start with this character
769 // while CompareChar found do:
770 // Split String with PathChar
771 // Search Environmentvariable
772 // if found: Replace Strings
774 char const CompareChar = '$';
775 char const FirstChar = '{';
776 char const EndChar = '}';
777 char const UnderscoreChar = '_';
778 string EndString; EndString += EndChar;
779 string FirstString; FirstString += FirstChar;
780 string CompareString; CompareString += CompareChar;
781 string const RegExp("*}*"); // Exist EndChar inside a String?
783 // first: Search for a '$' - Sign.
785 string result1; //(copy); // for split-calls
786 string result0 = split(path, result1, CompareChar);
787 while (!result0.empty()) {
788 string copy1(result0); // contains String after $
790 // Check, if there is an EndChar inside original String.
792 if (!regexMatch(copy1, RegExp)) {
793 // No EndChar inside. So we are finished
794 result1 += CompareString + result0;
800 string res0 = split(copy1, res1, EndChar);
801 // Now res1 holds the environmentvariable
802 // First, check, if Contents is ok.
803 if (res1.empty()) { // No environmentvariable. Continue Loop.
804 result1 += CompareString + FirstString;
808 // check contents of res1
809 char const * res1_contents = res1.c_str();
810 if (*res1_contents != FirstChar) {
811 // Again No Environmentvariable
812 result1 += CompareString;
816 // Check for variable names
817 // Situation ${} is detected as "No Environmentvariable"
818 char const * cp1 = res1_contents + 1;
819 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
821 while (*cp1 && result) {
822 result = isalnum(*cp1) ||
823 (*cp1 == UnderscoreChar);
828 // no correct variable name
829 result1 += CompareString + res1 + EndString;
830 result0 = split(res0, res1, CompareChar);
835 string env(GetEnv(res1_contents + 1));
837 // Congratulations. Environmentvariable found
840 result1 += CompareString + res1 + EndString;
843 result0 = split(res0, res1, CompareChar);
847 } // ReplaceEnvironmentPath
850 // Make relative path out of two absolute paths
851 string const MakeRelPath(string const & abspath0, string const & basepath0)
852 // Makes relative path out of absolute path. If it is deeper than basepath,
853 // it's easy. If basepath and abspath share something (they are all deeper
854 // than some directory), it'll be rendered using ..'s. If they are completely
855 // different, then the absolute path will be used as relative path.
857 // This is a hack. It should probaly be done in another way. Lgb.
858 string abspath = CleanupPath(abspath0);
859 string basepath = CleanupPath(basepath0);
861 return "<unknown_path>";
863 string::size_type const abslen = abspath.length();
864 string::size_type const baselen = basepath.length();
866 // Find first different character
867 string::size_type i = 0;
868 while (i < abslen && i < baselen && abspath[i] == basepath[i]) ++i;
871 if (i < abslen && i < baselen
872 || (i<abslen && abspath[i] != '/' && i == baselen)
873 || (i<baselen && basepath[i] != '/' && i == abslen))
875 if (i) --i; // here was the last match
876 while (i && abspath[i] != '/') --i;
880 // actually no match - cannot make it relative
884 // Count how many dirs there are in basepath above match
885 // and append as many '..''s into relpath
887 string::size_type j = i;
888 while (j < baselen) {
889 if (basepath[j] == '/') {
890 if (j + 1 == baselen) break;
896 // Append relative stuff from common directory to abspath
897 if (abspath[i] == '/') ++i;
898 for (; i < abslen; ++i)
901 if (suffixIs(buf, '/'))
902 buf.erase(buf.length() - 1);
903 // Substitute empty with .
910 // Append sub-directory(ies) to a path in an intelligent way
911 string const AddPath(string const & path, string const & path_2)
914 string const path2 = CleanupPath(path_2);
916 if (!path.empty() && path != "." && path != "./") {
917 buf = CleanupPath(path);
918 if (path[path.length() - 1] != '/')
923 string::size_type p2start = path2.find_first_not_of('/');
925 string::size_type p2end = path2.find_last_not_of('/');
927 string tmp = path2.substr(p2start, p2end - p2start + 1);
935 Change extension of oldname to extension.
936 Strips path off if no_path == true.
937 If no extension on oldname, just appends.
940 ChangeExtension(string const & oldname, string const & extension)
942 string::size_type const last_slash = oldname.rfind('/');
943 string::size_type last_dot = oldname.rfind('.');
944 if (last_dot < last_slash && last_slash != string::npos)
945 last_dot = string::npos;
948 // Make sure the extension starts with a dot
949 if (!extension.empty() && extension[0] != '.')
950 ext= "." + extension;
954 return CleanupPath(oldname.substr(0, last_dot) + ext);
958 /// Return the extension of the file (not including the .)
959 string const GetExtension(string const & name)
961 string::size_type const last_slash = name.rfind('/');
962 string::size_type const last_dot = name.rfind('.');
963 if (last_dot != string::npos &&
964 (last_slash == string::npos || last_dot > last_slash))
965 return name.substr(last_dot + 1,
966 name.length() - (last_dot + 1));
972 // Creates a nice compact path for displaying
974 MakeDisplayPath (string const & path, unsigned int threshold)
976 string::size_type const l1 = path.length();
978 // First, we try a relative path compared to home
979 string const home(GetEnvPath("HOME"));
980 string relhome = MakeRelPath(path, home);
982 string::size_type l2 = relhome.length();
986 // If we backup from home or don't have a relative path,
987 // this try is no good
988 if (prefixIs(relhome, "../") || AbsolutePath(relhome)) {
989 // relative path was no good, just use the original path
996 // Is the path too long?
997 if (l2 > threshold) {
1003 while (relhome.length() > threshold)
1004 relhome = split(relhome, temp, '/');
1006 // Did we shortend everything away?
1007 if (relhome.empty()) {
1008 // Yes, filename in itself is too long.
1009 // Pick the start and the end of the filename.
1010 relhome = OnlyFilename(path);
1011 string head = relhome.substr(0, threshold/2 - 3);
1013 l2 = relhome.length();
1015 relhome.substr(l2 - threshold/2 - 2, l2 - 1);
1016 relhome = head + "..." + tail;
1019 return prefix + relhome;
1023 bool LyXReadLink(string const & File, string & Link)
1025 char LinkBuffer[512];
1026 // Should be PATH_MAX but that needs autconf support
1027 int const nRead = ::readlink(File.c_str(),
1028 LinkBuffer, sizeof(LinkBuffer) - 1);
1031 LinkBuffer[nRead] = 0;
1037 typedef pair<int, string> cmdret;
1039 cmdret const do_popen(string const & cmd)
1041 // One question is if we should use popen or
1042 // create our own popen based on fork, exec, pipe
1043 // of course the best would be to have a
1044 // pstream (process stream), with the
1045 // variants ipstream, opstream
1046 FILE * inf = ::popen(cmd.c_str(), "r");
1050 ret += static_cast<char>(c);
1053 int pret = pclose(inf);
1054 return make_pair(pret, ret);
1059 findtexfile(string const & fil, string const & /*format*/)
1061 /* There is no problem to extend this function too use other
1062 methods to look for files. It could be setup to look
1063 in environment paths and also if wanted as a last resort
1064 to a recursive find. One of the easier extensions would
1065 perhaps be to use the LyX file lookup methods. But! I am
1066 going to implement this until I see some demand for it.
1070 // If the file can be found directly, we just return a
1071 // absolute path version of it.
1072 if (FileInfo(fil).exist())
1073 return MakeAbsPath(fil);
1075 // No we try to find it using kpsewhich.
1076 // It seems from the kpsewhich manual page that it is safe to use
1077 // kpsewhich without --format: "When the --format option is not
1078 // given, the search path used when looking for a file is inferred
1079 // from the name given, by looking for a known extension. If no
1080 // known extension is found, the search path for TeX source files
1082 // However, we want to take advantage of the format sine almost all
1083 // the different formats has environment variables that can be used
1084 // to controll which paths to search. f.ex. bib looks in
1085 // BIBINPUTS and TEXBIB. Small list follows:
1086 // bib - BIBINPUTS, TEXBIB
1088 // graphic/figure - TEXPICTS, TEXINPUTS
1089 // ist - TEXINDEXSTYLE, INDEXSTYLE
1090 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1092 // tfm - TFMFONTS, TEXFONTS
1093 // This means that to use kpsewhich in the best possible way we
1094 // should help it by setting additional path in the approp. envir.var.
1095 string const kpsecmd = "kpsewhich " + fil;
1097 cmdret const c = do_popen(kpsecmd);
1099 lyxerr[Debug::LATEX] << "kpse status = " << c.first << "\n"
1100 << "kpse result = `" << strip(c.second, '\n')
1102 return c.first != -1 ? strip(c.second, '\n') : string();
1106 void removeAutosaveFile(string const & filename)
1108 string a = OnlyPath(filename);
1110 a += OnlyFilename(filename);
1112 FileInfo fileinfo(a);
1113 if (fileinfo.exist()) {
1114 if (lyx::unlink(a) != 0) {
1115 WriteFSAlert(_("Could not delete auto-save file!"), a);