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)
137 //lyxerr << "fi : " << fi << endl;
138 //lyxerr << "fi.exists" << fi.exist() << endl;
139 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
141 if (fi.readable()) // read-only
143 return -1; // everything else.
147 //returns true: dir writeable
148 // false: not writeable
149 bool IsDirWriteable (string const & path)
151 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
153 string const tmpfl(lyx::tempName(path, "lyxwritetest"));
163 // Uses a string of paths separated by ";"s to find a file to open.
164 // Can't cope with pathnames with a ';' in them. Returns full path to file.
165 // If path entry begins with $$LyX/, use system_lyxdir
166 // If path entry begins with $$User/, use user_lyxdir
167 // Example: "$$User/doc;$$LyX/doc"
168 string const FileOpenSearch (string const & path, string const & name,
171 string real_file, path_element;
172 bool notfound = true;
173 string tmppath = split(path, path_element, ';');
175 while (notfound && !path_element.empty()) {
176 path_element = os::slashify_path(path_element);
177 if (!suffixIs(path_element, '/'))
179 path_element = subst(path_element, "$$LyX", system_lyxdir);
180 path_element = subst(path_element, "$$User", user_lyxdir);
182 real_file = FileSearch(path_element, name, ext);
184 if (real_file.empty()) {
186 tmppath = split(tmppath, path_element, ';');
187 } while(!tmppath.empty() && path_element.empty());
193 if (ext.empty() && notfound) {
194 real_file = FileOpenSearch(path, name, "exe");
195 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
202 /// Returns a vector of all files in directory dir having extension ext.
203 vector<string> const DirList(string const & dir, string const & ext)
205 // This is a non-error checking C/system implementation
206 string extension(ext);
207 if (!extension.empty() && extension[0] != '.')
208 extension.insert(0, ".");
209 vector<string> dirlist;
210 DIR * dirp = ::opendir(dir.c_str());
212 while ((dire = ::readdir(dirp))) {
213 string const fil = dire->d_name;
214 if (suffixIs(fil, extension)) {
215 dirlist.push_back(fil);
220 /* I would have prefered to take a vector<string>& as parameter so
221 that we could avoid the copy of the vector when returning.
223 dirlist.swap(argvec);
224 to avoid the copy. (Lgb)
226 /* A C++ implementaion will look like this:
227 string extension(ext);
228 if (extension[0] != '.') extension.insert(0, ".");
229 vector<string> dirlist;
230 directory_iterator dit("dir");
231 while (dit != directory_iterator()) {
232 string fil = dit->filename;
233 if (prefixIs(fil, extension)) {
234 dirlist.push_back(fil);
238 dirlist.swap(argvec);
244 // Returns the real name of file name in directory path, with optional
246 string const FileSearch(string const & path, string const & name,
249 // if `name' is an absolute path, we ignore the setting of `path'
250 // Expand Environmentvariables in 'name'
251 string const tmpname = ReplaceEnvironmentPath(name);
252 string fullname = MakeAbsPath(tmpname, path);
253 // search first without extension, then with it.
254 if (IsFileReadable(fullname))
256 else if (ext.empty())
258 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
261 if (IsFileReadable(fullname))
269 // Search the file name.ext in the subdirectory dir of
271 // 2) build_lyxdir (if not empty)
273 string const LibFileSearch(string const & dir, string const & name,
276 string fullname = FileSearch(AddPath(user_lyxdir, dir),
278 if (!fullname.empty())
281 if (!build_lyxdir.empty())
282 fullname = FileSearch(AddPath(build_lyxdir, dir),
284 if (!fullname.empty())
287 return FileSearch(AddPath(system_lyxdir, dir), name, ext);
292 i18nLibFileSearch(string const & dir, string const & name,
295 string const lang = token(string(GetEnv("LANG")), '_', 0);
297 if (lang.empty() || lang == "C")
298 return LibFileSearch(dir, name, ext);
300 string const tmp = LibFileSearch(dir, lang + '_' + name,
305 return LibFileSearch(dir, name, ext);
310 string const GetEnv(string const & envname)
312 // f.ex. what about error checking?
313 char const * const ch = getenv(envname.c_str());
314 string const envstr = !ch ? "" : ch;
319 string const GetEnvPath(string const & name)
322 string const pathlist = subst(GetEnv(name), ':', ';');
324 string const pathlist = os::slashify_path(GetEnv(name));
326 return strip(pathlist, ';');
330 bool PutEnv(string const & envstr)
332 // CHECK Look at and fix this.
333 // f.ex. what about error checking?
336 // this leaks, but what can we do about it?
337 // Is doing a getenv() and a free() of the older value
338 // a good idea? (JMarc)
339 // Actually we don't have to leak...calling putenv like this
340 // should be enough: ... and this is obviously not enough if putenv
341 // does not make a copy of the string. It is also not very wise to
342 // put a string on the free store. If we have to leak we should do it
344 char * leaker = new char[envstr.length() + 1];
345 envstr.copy(leaker, envstr.length());
346 leaker[envstr.length()] = '\0';
347 int const retval = lyx::putenv(leaker);
349 // If putenv does not make a copy of the char const * this
350 // is very dangerous. OTOH if it does take a copy this is the
352 // The only implementation of putenv that I have seen does not
353 // allocate memory. _And_ after testing the putenv in glibc it
354 // seems that we need to make a copy of the string contents.
355 // I will enable the above.
356 //int retval = lyx::putenv(envstr.c_str());
360 string const str = envstr.split(varname,'=');
361 int const retval = ::setenv(varname.c_str(), str.c_str(), true);
363 // No environment setting function. Can this happen?
364 int const retval = 1; //return an error condition.
371 bool PutEnvPath(string const & envstr)
373 return PutEnv(envstr);
379 int DeleteAllFilesInDir (string const & path)
381 // I have decided that we will be using parts from the boost
382 // library. Check out http://www.boost.org/
383 // For directory access we will then use the directory_iterator.
384 // Then the code will be something like:
385 // directory_iterator dit(path);
386 // directory_iterator dend;
387 // if (dit == dend) {
388 // WriteFSAlert(_("Error! Cannot open directory:"), path);
391 // for (; dit != dend; ++dit) {
392 // string filename(*dit);
393 // if (filename == "." || filename == "..")
395 // string unlinkpath(AddName(path, filename));
396 // if (lyx::unlink(unlinkpath))
397 // WriteFSAlert(_("Error! Could not remove file:"),
401 DIR * dir = ::opendir(path.c_str());
403 WriteFSAlert (_("Error! Cannot open directory:"), path);
407 int return_value = 0;
408 while ((de = readdir(dir))) {
409 string const temp = de->d_name;
410 if (temp == "." || temp == "..")
412 string const unlinkpath = AddName (path, temp);
414 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
418 if (FileInfo(unlinkpath).isDir())
419 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
420 deleted &= (lyx::unlink(unlinkpath) == 0);
422 WriteFSAlert (_("Error! Could not remove file:"),
432 string const CreateTmpDir(string const & tempdir, string const & mask)
435 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
436 << "CreateTmpDir: mask=`" << mask << "'" << endl;
438 string const tmpfl(lyx::tempName(tempdir, mask));
439 // lyx::tempName actually creates a file to make sure that it
440 // stays unique. So we have to delete it before we can create
441 // a dir with the same name. Note also that we are not thread
442 // safe because of the gap between unlink and mkdir. (Lgb)
443 lyx::unlink(tmpfl.c_str());
445 if (tmpfl.empty() || lyx::mkdir(tmpfl, 0700)) {
446 WriteFSAlert(_("Error! Couldn't create temporary directory:"),
450 return MakeAbsPath(tmpfl);
454 int DestroyTmpDir(string const & tmpdir, bool Allfiles)
459 if (Allfiles && DeleteAllFilesInDir(tmpdir)) return -1;
460 if (lyx::rmdir(tmpdir)) {
461 WriteFSAlert(_("Error! Couldn't delete temporary directory:"),
471 string const CreateBufferTmpDir(string const & pathfor)
474 static string const tmpdir(pathfor.empty() ? os::getTmpDir() : pathfor);
475 // We are in our own directory. Why bother to mangle name?
476 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
478 string const tmpfl = tmpdir + "/lyx_tmpbuf" + tostr(count++);
479 if (lyx::mkdir(tmpfl, 0777)) {
480 WriteFSAlert(_("Error! Couldn't create temporary directory:"),
488 int DestroyBufferTmpDir(string const & tmpdir)
490 return DestroyTmpDir(tmpdir, true);
494 string const CreateLyXTmpDir(string const & deflt)
496 if ((!deflt.empty()) && (deflt != "/tmp")) {
497 if (lyx::mkdir(deflt, 0777)) {
501 string const t(CreateTmpDir(deflt, "lyx_tmpdir"));
509 string const t(CreateTmpDir("/tmp", "lyx_tmpdir"));
515 int DestroyLyXTmpDir(string const & tmpdir)
517 return DestroyTmpDir (tmpdir, false); // Why false?
521 // Creates directory. Returns true if succesfull
522 bool createDirectory(string const & path, int permission)
524 string temp(strip(os::slashify_path(path), '/'));
527 WriteAlert(_("Internal error!"),
528 _("Call to createDirectory with invalid name"));
532 if (lyx::mkdir(temp, permission)) {
533 WriteFSAlert (_("Error! Couldn't create directory:"), temp);
540 // Strip filename from path name
541 string const OnlyPath(string const & Filename)
543 // If empty filename, return empty
544 if (Filename.empty()) return Filename;
546 // Find last / or start of filename
547 string::size_type j = Filename.rfind('/');
548 if (j == string::npos)
550 return Filename.substr(0, j + 1);
554 // Convert relative path into absolute path based on a basepath.
555 // If relpath is absolute, just use that.
556 // If basepath is empty, use CWD as base.
557 string const MakeAbsPath(string const & RelPath, string const & BasePath)
559 // checks for already absolute path
560 if (AbsolutePath(RelPath))
563 // Copies given paths
564 string TempRel(os::slashify_path(RelPath));
565 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
566 TempRel = subst(TempRel, "//", "/");
570 if (AbsolutePath(BasePath))
573 TempBase = AddPath(lyx::getcwd(), BasePath);
575 // Handle /./ at the end of the path
576 while(suffixIs(TempBase, "/./"))
577 TempBase.erase(TempBase.length() - 2);
579 // processes relative path
580 string RTemp(TempRel);
583 while (!RTemp.empty()) {
585 RTemp = split(RTemp, Temp, '/');
587 if (Temp == ".") continue;
589 // Remove one level of TempBase
590 string::difference_type i = TempBase.length() - 2;
593 while (i > 0 && TempBase[i] != '/') --i;
597 while (i > 2 && TempBase[i] != '/') --i;
600 TempBase.erase(i, string::npos);
603 } else if (Temp.empty() && !RTemp.empty()) {
604 TempBase = os::current_root() + RTemp;
607 // Add this piece to TempBase
608 if (!suffixIs(TempBase, '/'))
614 // returns absolute path
615 return os::slashify_path(TempBase);
619 // Correctly append filename to the pathname.
620 // If pathname is '.', then don't use pathname.
621 // Chops any path of filename.
622 string const AddName(string const & path, string const & fname)
625 string const basename(OnlyFilename(fname));
629 if (path != "." && path != "./" && !path.empty()) {
630 buf = os::slashify_path(path);
631 if (!suffixIs(path, '/'))
635 return buf + basename;
639 // Strips path from filename
640 string const OnlyFilename(string const & fname)
645 string::size_type j = fname.rfind('/');
646 if (j == string::npos) // no '/' in fname
650 return fname.substr(j + 1);
654 // Is a filename/path absolute?
655 bool AbsolutePath(string const & path)
658 return (!path.empty() && path[0] == '/');
660 return (!path.empty() && isalpha(static_cast<unsigned char>(path[0])) && path.length() > 1 && path[1] == ':');
665 // Create absolute path. If impossible, don't do anything
666 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
667 string const ExpandPath(string const & path)
669 // checks for already absolute path
670 string RTemp(ReplaceEnvironmentPath(path));
671 if (AbsolutePath(RTemp))
675 string const copy(RTemp);
678 RTemp = split(RTemp, Temp, '/');
681 return lyx::getcwd() /*GetCWD()*/ + '/' + RTemp;
682 } else if (Temp == "~") {
683 return GetEnvPath("HOME") + '/' + RTemp;
684 } else if (Temp == "..") {
685 return MakeAbsPath(copy);
687 // Don't know how to handle this
693 // Constracts path/../path
694 // Can't handle "../../" or "/../" (Asger)
695 string const NormalizePath(string const & path)
701 if (AbsolutePath(path))
704 // Make implicit current directory explicit
707 while (!RTemp.empty()) {
709 RTemp = split(RTemp, Temp, '/');
713 } else if (Temp == "..") {
714 // Remove one level of TempBase
715 string::difference_type i = TempBase.length() - 2;
716 while (i > 0 && TempBase[i] != '/')
718 if (i >= 0 && TempBase[i] == '/')
719 TempBase.erase(i + 1, string::npos);
723 TempBase += Temp + '/';
727 // returns absolute path
732 string const GetFileContents(string const & fname)
734 FileInfo finfo(fname);
736 ifstream ifs(fname.c_str());
741 return ofs.str().c_str();
744 lyxerr << "LyX was not able to read file '" << fname << "'" << endl;
750 // Search ${...} as Variable-Name inside the string and replace it with
751 // the denoted environmentvariable
752 // Allow Variables according to
753 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
756 string const ReplaceEnvironmentPath(string const & path)
759 // CompareChar: Environmentvariables starts with this character
760 // PathChar: Next path component start with this character
761 // while CompareChar found do:
762 // Split String with PathChar
763 // Search Environmentvariable
764 // if found: Replace Strings
766 char const CompareChar = '$';
767 char const FirstChar = '{';
768 char const EndChar = '}';
769 char const UnderscoreChar = '_';
770 string EndString; EndString += EndChar;
771 string FirstString; FirstString += FirstChar;
772 string CompareString; CompareString += CompareChar;
773 string const RegExp("*}*"); // Exist EndChar inside a String?
775 // first: Search for a '$' - Sign.
777 string result1; //(copy); // for split-calls
778 string result0 = split(path, result1, CompareChar);
779 while (!result0.empty()) {
780 string copy1(result0); // contains String after $
782 // Check, if there is an EndChar inside original String.
784 if (!regexMatch(copy1, RegExp)) {
785 // No EndChar inside. So we are finished
786 result1 += CompareString + result0;
792 string res0 = split(copy1, res1, EndChar);
793 // Now res1 holds the environmentvariable
794 // First, check, if Contents is ok.
795 if (res1.empty()) { // No environmentvariable. Continue Loop.
796 result1 += CompareString + FirstString;
800 // check contents of res1
801 char const * res1_contents = res1.c_str();
802 if (*res1_contents != FirstChar) {
803 // Again No Environmentvariable
804 result1 += CompareString;
808 // Check for variable names
809 // Situation ${} is detected as "No Environmentvariable"
810 char const * cp1 = res1_contents + 1;
811 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
813 while (*cp1 && result) {
814 result = isalnum(*cp1) ||
815 (*cp1 == UnderscoreChar);
820 // no correct variable name
821 result1 += CompareString + res1 + EndString;
822 result0 = split(res0, res1, CompareChar);
827 string env(GetEnv(res1_contents + 1));
829 // Congratulations. Environmentvariable found
832 result1 += CompareString + res1 + EndString;
835 result0 = split(res0, res1, CompareChar);
839 } // ReplaceEnvironmentPath
842 // Make relative path out of two absolute paths
843 string const MakeRelPath(string const & abspath, string const & basepath)
844 // Makes relative path out of absolute path. If it is deeper than basepath,
845 // it's easy. If basepath and abspath share something (they are all deeper
846 // than some directory), it'll be rendered using ..'s. If they are completely
847 // different, then the absolute path will be used as relative path.
849 string::size_type const abslen = abspath.length();
850 string::size_type const baselen = basepath.length();
852 string::size_type i = os::common_path(abspath, basepath);
855 // actually no match - cannot make it relative
859 // Count how many dirs there are in basepath above match
860 // and append as many '..''s into relpath
862 string::size_type j = i;
863 while (j < baselen) {
864 if (basepath[j] == '/') {
865 if (j + 1 == baselen) break;
871 // Append relative stuff from common directory to abspath
872 if (abspath[i] == '/') ++i;
873 for (; i < abslen; ++i)
876 if (suffixIs(buf, '/'))
877 buf.erase(buf.length() - 1);
878 // Substitute empty with .
885 // Append sub-directory(ies) to a path in an intelligent way
886 string const AddPath(string const & path, string const & path_2)
889 string const path2 = os::slashify_path(path_2);
891 if (!path.empty() && path != "." && path != "./") {
892 buf = os::slashify_path(path);
893 if (path[path.length() - 1] != '/')
897 if (!path2.empty()) {
898 string::size_type const p2start = path2.find_first_not_of('/');
899 string::size_type const p2end = path2.find_last_not_of('/');
900 string const tmp = path2.substr(p2start, p2end - p2start + 1);
908 Change extension of oldname to extension.
909 Strips path off if no_path == true.
910 If no extension on oldname, just appends.
913 ChangeExtension(string const & oldname, string const & extension)
915 string::size_type const last_slash = oldname.rfind('/');
916 string::size_type last_dot = oldname.rfind('.');
917 if (last_dot < last_slash && last_slash != string::npos)
918 last_dot = string::npos;
921 // Make sure the extension starts with a dot
922 if (!extension.empty() && extension[0] != '.')
923 ext= "." + extension;
927 return os::slashify_path(oldname.substr(0, last_dot) + ext);
931 /// Return the extension of the file (not including the .)
932 string const GetExtension(string const & name)
934 string::size_type const last_slash = name.rfind('/');
935 string::size_type const last_dot = name.rfind('.');
936 if (last_dot != string::npos &&
937 (last_slash == string::npos || last_dot > last_slash))
938 return name.substr(last_dot + 1,
939 name.length() - (last_dot + 1));
945 // Creates a nice compact path for displaying
947 MakeDisplayPath (string const & path, unsigned int threshold)
949 string::size_type const l1 = path.length();
951 // First, we try a relative path compared to home
952 string const home(GetEnvPath("HOME"));
953 string relhome = MakeRelPath(path, home);
955 string::size_type l2 = relhome.length();
959 // If we backup from home or don't have a relative path,
960 // this try is no good
961 if (prefixIs(relhome, "../") || AbsolutePath(relhome)) {
962 // relative path was no good, just use the original path
969 // Is the path too long?
970 if (l2 > threshold) {
976 while (relhome.length() > threshold)
977 relhome = split(relhome, temp, '/');
979 // Did we shortend everything away?
980 if (relhome.empty()) {
981 // Yes, filename in itself is too long.
982 // Pick the start and the end of the filename.
983 relhome = OnlyFilename(path);
984 string const head = relhome.substr(0, threshold/2 - 3);
986 l2 = relhome.length();
988 relhome.substr(l2 - threshold/2 - 2, l2 - 1);
989 relhome = head + "..." + tail;
992 return prefix + relhome;
996 bool LyXReadLink(string const & File, string & Link)
998 char LinkBuffer[512];
999 // Should be PATH_MAX but that needs autconf support
1000 int const nRead = ::readlink(File.c_str(),
1001 LinkBuffer, sizeof(LinkBuffer) - 1);
1004 LinkBuffer[nRead] = '\0'; // terminator
1012 typedef pair<int, string> cmdret;
1014 cmdret const do_popen(string const & cmd)
1016 // One question is if we should use popen or
1017 // create our own popen based on fork, exec, pipe
1018 // of course the best would be to have a
1019 // pstream (process stream), with the
1020 // variants ipstream, opstream
1021 FILE * inf = ::popen(cmd.c_str(), "r");
1025 ret += static_cast<char>(c);
1028 int const pret = pclose(inf);
1029 return make_pair(pret, ret);
1036 findtexfile(string const & fil, string const & /*format*/)
1038 /* There is no problem to extend this function too use other
1039 methods to look for files. It could be setup to look
1040 in environment paths and also if wanted as a last resort
1041 to a recursive find. One of the easier extensions would
1042 perhaps be to use the LyX file lookup methods. But! I am
1043 going to implement this until I see some demand for it.
1047 // If the file can be found directly, we just return a
1048 // absolute path version of it.
1049 if (FileInfo(fil).exist())
1050 return MakeAbsPath(fil);
1052 // No we try to find it using kpsewhich.
1053 // It seems from the kpsewhich manual page that it is safe to use
1054 // kpsewhich without --format: "When the --format option is not
1055 // given, the search path used when looking for a file is inferred
1056 // from the name given, by looking for a known extension. If no
1057 // known extension is found, the search path for TeX source files
1059 // However, we want to take advantage of the format sine almost all
1060 // the different formats has environment variables that can be used
1061 // to controll which paths to search. f.ex. bib looks in
1062 // BIBINPUTS and TEXBIB. Small list follows:
1063 // bib - BIBINPUTS, TEXBIB
1065 // graphic/figure - TEXPICTS, TEXINPUTS
1066 // ist - TEXINDEXSTYLE, INDEXSTYLE
1067 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1069 // tfm - TFMFONTS, TEXFONTS
1070 // This means that to use kpsewhich in the best possible way we
1071 // should help it by setting additional path in the approp. envir.var.
1072 string const kpsecmd = "kpsewhich " + fil;
1074 cmdret const c = do_popen(kpsecmd);
1076 lyxerr[Debug::LATEX] << "kpse status = " << c.first << "\n"
1077 << "kpse result = `" << strip(c.second, '\n')
1080 return strip(strip(c.second, '\n'), '\r');
1086 void removeAutosaveFile(string const & filename)
1088 string a = OnlyPath(filename);
1090 a += OnlyFilename(filename);
1092 FileInfo const fileinfo(a);
1093 if (fileinfo.exist()) {
1094 if (lyx::unlink(a) != 0) {
1095 WriteFSAlert(_("Could not delete auto-save file!"), a);