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(filename, ".lyx");
75 bool IsSGMLFilename(string const & filename)
77 return suffixIs(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() << "Deleting file: " << unlinkpath << endl;
414 if (FileInfo(unlinkpath).isDir())
415 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
416 deleted &= (lyx::unlink(unlinkpath) == 0);
418 WriteFSAlert (_("Error! Could not remove file:"),
428 string const CreateTmpDir(string const & tempdir, string const & mask)
431 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
432 << "CreateTmpDir: mask=`" << mask << "'" << endl;
434 string const tmpfl(lyx::tempName(tempdir, mask));
435 // lyx::tempName actually creates a file to make sure that it
436 // stays unique. So we have to delete it before we can create
437 // a dir with the same name. Note also that we are not thread
438 // safe because of the gap between unlink and mkdir. (Lgb)
439 lyx::unlink(tmpfl.c_str());
441 if (tmpfl.empty() || lyx::mkdir(tmpfl, 0700)) {
442 WriteFSAlert(_("Error! Couldn't create temporary directory:"),
446 return MakeAbsPath(tmpfl);
450 int DestroyTmpDir(string const & tmpdir, bool Allfiles)
455 if (Allfiles && DeleteAllFilesInDir(tmpdir)) return -1;
456 if (lyx::rmdir(tmpdir)) {
457 WriteFSAlert(_("Error! Couldn't delete temporary directory:"),
467 string const CreateBufferTmpDir(string const & pathfor)
470 static string const tmpdir(pathfor.empty() ? os::getTmpDir() : pathfor);
471 // We are in our own directory. Why bother to mangle name?
472 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
474 string const tmpfl = tmpdir + "/lyx_tmpbuf" + tostr(count++);
475 if (lyx::mkdir(tmpfl, 0777)) {
476 WriteFSAlert(_("Error! Couldn't create temporary directory:"),
484 int DestroyBufferTmpDir(string const & tmpdir)
486 return DestroyTmpDir(tmpdir, true);
490 string const CreateLyXTmpDir(string const & deflt)
492 if ((!deflt.empty()) && (deflt != "/tmp")) {
493 if (lyx::mkdir(deflt, 0777)) {
497 string const t(CreateTmpDir(deflt, "lyx_tmpdir"));
505 string const t(CreateTmpDir("/tmp", "lyx_tmpdir"));
511 int DestroyLyXTmpDir(string const & tmpdir)
513 return DestroyTmpDir (tmpdir, false); // Why false?
517 // Creates directory. Returns true if succesfull
518 bool createDirectory(string const & path, int permission)
520 string temp(strip(os::slashify_path(path), '/'));
523 WriteAlert(_("Internal error!"),
524 _("Call to createDirectory with invalid name"));
528 if (lyx::mkdir(temp, permission)) {
529 WriteFSAlert (_("Error! Couldn't create directory:"), temp);
536 // Strip filename from path name
537 string const OnlyPath(string const & Filename)
539 // If empty filename, return empty
540 if (Filename.empty()) return Filename;
542 // Find last / or start of filename
543 string::size_type j = Filename.rfind('/');
544 if (j == string::npos)
546 return Filename.substr(0, j + 1);
550 // Convert relative path into absolute path based on a basepath.
551 // If relpath is absolute, just use that.
552 // If basepath is empty, use CWD as base.
553 string const MakeAbsPath(string const & RelPath, string const & BasePath)
555 // checks for already absolute path
556 if (AbsolutePath(RelPath))
559 // Copies given paths
560 string TempRel(os::slashify_path(RelPath));
561 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
562 TempRel = subst(TempRel, "//", "/");
566 if (AbsolutePath(BasePath))
569 TempBase = AddPath(lyx::getcwd(), BasePath);
571 // Handle /./ at the end of the path
572 while(suffixIs(TempBase, "/./"))
573 TempBase.erase(TempBase.length() - 2);
575 // processes relative path
576 string RTemp(TempRel);
579 while (!RTemp.empty()) {
581 RTemp = split(RTemp, Temp, '/');
583 if (Temp == ".") continue;
585 // Remove one level of TempBase
586 string::difference_type i = TempBase.length() - 2;
589 while (i > 0 && TempBase[i] != '/') --i;
593 while (i > 2 && TempBase[i] != '/') --i;
596 TempBase.erase(i, string::npos);
599 } else if (Temp.empty() && !RTemp.empty()) {
600 TempBase = os::current_root() + RTemp;
603 // Add this piece to TempBase
604 if (!suffixIs(TempBase, '/'))
610 // returns absolute path
611 return os::slashify_path(TempBase);
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 = os::slashify_path(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() && 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 lyx::getcwd() /*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 GetFileContents(string const & fname)
730 FileInfo finfo(fname);
732 ifstream ifs(fname.c_str());
733 std::ostringstream ofs;
737 return ofs.str().c_str();
740 lyxerr << "LyX was not able to read file '" << fname << "'" << endl;
746 // Search ${...} as Variable-Name inside the string and replace it with
747 // the denoted environmentvariable
748 // Allow Variables according to
749 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
752 string const ReplaceEnvironmentPath(string const & path)
755 // CompareChar: Environmentvariables starts with this character
756 // PathChar: Next path component start with this character
757 // while CompareChar found do:
758 // Split String with PathChar
759 // Search Environmentvariable
760 // if found: Replace Strings
762 char const CompareChar = '$';
763 char const FirstChar = '{';
764 char const EndChar = '}';
765 char const UnderscoreChar = '_';
766 string EndString; EndString += EndChar;
767 string FirstString; FirstString += FirstChar;
768 string CompareString; CompareString += CompareChar;
769 string const RegExp("*}*"); // Exist EndChar inside a String?
771 // first: Search for a '$' - Sign.
773 string result1; //(copy); // for split-calls
774 string result0 = split(path, result1, CompareChar);
775 while (!result0.empty()) {
776 string copy1(result0); // contains String after $
778 // Check, if there is an EndChar inside original String.
780 if (!regexMatch(copy1, RegExp)) {
781 // No EndChar inside. So we are finished
782 result1 += CompareString + result0;
788 string res0 = split(copy1, res1, EndChar);
789 // Now res1 holds the environmentvariable
790 // First, check, if Contents is ok.
791 if (res1.empty()) { // No environmentvariable. Continue Loop.
792 result1 += CompareString + FirstString;
796 // check contents of res1
797 char const * res1_contents = res1.c_str();
798 if (*res1_contents != FirstChar) {
799 // Again No Environmentvariable
800 result1 += CompareString;
804 // Check for variable names
805 // Situation ${} is detected as "No Environmentvariable"
806 char const * cp1 = res1_contents + 1;
807 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
809 while (*cp1 && result) {
810 result = isalnum(*cp1) ||
811 (*cp1 == UnderscoreChar);
816 // no correct variable name
817 result1 += CompareString + res1 + EndString;
818 result0 = split(res0, res1, CompareChar);
823 string env(GetEnv(res1_contents + 1));
825 // Congratulations. Environmentvariable found
828 result1 += CompareString + res1 + EndString;
831 result0 = split(res0, res1, CompareChar);
835 } // ReplaceEnvironmentPath
838 // Make relative path out of two absolute paths
839 string const MakeRelPath(string const & abspath, string const & basepath)
840 // Makes relative path out of absolute path. If it is deeper than basepath,
841 // it's easy. If basepath and abspath share something (they are all deeper
842 // than some directory), it'll be rendered using ..'s. If they are completely
843 // different, then the absolute path will be used as relative path.
845 string::size_type const abslen = abspath.length();
846 string::size_type const baselen = basepath.length();
848 string::size_type i = os::common_path(abspath, basepath);
851 // actually no match - cannot make it relative
855 // Count how many dirs there are in basepath above match
856 // and append as many '..''s into relpath
858 string::size_type j = i;
859 while (j < baselen) {
860 if (basepath[j] == '/') {
861 if (j + 1 == baselen) break;
867 // Append relative stuff from common directory to abspath
868 if (abspath[i] == '/') ++i;
869 for (; i < abslen; ++i)
872 if (suffixIs(buf, '/'))
873 buf.erase(buf.length() - 1);
874 // Substitute empty with .
881 // Append sub-directory(ies) to a path in an intelligent way
882 string const AddPath(string const & path, string const & path_2)
885 string const path2 = os::slashify_path(path_2);
887 if (!path.empty() && path != "." && path != "./") {
888 buf = os::slashify_path(path);
889 if (path[path.length() - 1] != '/')
893 if (!path2.empty()) {
894 string::size_type const p2start = path2.find_first_not_of('/');
895 string::size_type const p2end = path2.find_last_not_of('/');
896 string const tmp = path2.substr(p2start, p2end - p2start + 1);
904 Change extension of oldname to extension.
905 Strips path off if no_path == true.
906 If no extension on oldname, just appends.
909 ChangeExtension(string const & oldname, string const & extension)
911 string::size_type const last_slash = oldname.rfind('/');
912 string::size_type last_dot = oldname.rfind('.');
913 if (last_dot < last_slash && last_slash != string::npos)
914 last_dot = string::npos;
917 // Make sure the extension starts with a dot
918 if (!extension.empty() && extension[0] != '.')
919 ext= "." + extension;
923 return os::slashify_path(oldname.substr(0, last_dot) + ext);
927 /// Return the extension of the file (not including the .)
928 string const GetExtension(string const & name)
930 string::size_type const last_slash = name.rfind('/');
931 string::size_type const last_dot = name.rfind('.');
932 if (last_dot != string::npos &&
933 (last_slash == string::npos || last_dot > last_slash))
934 return name.substr(last_dot + 1,
935 name.length() - (last_dot + 1));
941 // Creates a nice compact path for displaying
943 MakeDisplayPath (string const & path, unsigned int threshold)
945 string::size_type const l1 = path.length();
947 // First, we try a relative path compared to home
948 string const home(GetEnvPath("HOME"));
949 string relhome = MakeRelPath(path, home);
951 string::size_type l2 = relhome.length();
955 // If we backup from home or don't have a relative path,
956 // this try is no good
957 if (prefixIs(relhome, "../") || AbsolutePath(relhome)) {
958 // relative path was no good, just use the original path
965 // Is the path too long?
966 if (l2 > threshold) {
972 while (relhome.length() > threshold)
973 relhome = split(relhome, temp, '/');
975 // Did we shortend everything away?
976 if (relhome.empty()) {
977 // Yes, filename in itself is too long.
978 // Pick the start and the end of the filename.
979 relhome = OnlyFilename(path);
980 string const head = relhome.substr(0, threshold/2 - 3);
982 l2 = relhome.length();
984 relhome.substr(l2 - threshold/2 - 2, l2 - 1);
985 relhome = head + "..." + tail;
988 return prefix + relhome;
992 bool LyXReadLink(string const & File, string & Link)
994 char LinkBuffer[512];
995 // Should be PATH_MAX but that needs autconf support
996 int const nRead = ::readlink(File.c_str(),
997 LinkBuffer, sizeof(LinkBuffer) - 1);
1000 LinkBuffer[nRead] = '\0'; // terminator
1008 typedef pair<int, string> cmdret;
1010 cmdret const do_popen(string const & cmd)
1012 // One question is if we should use popen or
1013 // create our own popen based on fork, exec, pipe
1014 // of course the best would be to have a
1015 // pstream (process stream), with the
1016 // variants ipstream, opstream
1017 FILE * inf = ::popen(cmd.c_str(), "r");
1021 ret += static_cast<char>(c);
1024 int const pret = pclose(inf);
1025 return make_pair(pret, ret);
1032 findtexfile(string const & fil, string const & /*format*/)
1034 /* There is no problem to extend this function too use other
1035 methods to look for files. It could be setup to look
1036 in environment paths and also if wanted as a last resort
1037 to a recursive find. One of the easier extensions would
1038 perhaps be to use the LyX file lookup methods. But! I am
1039 going to implement this until I see some demand for it.
1043 // If the file can be found directly, we just return a
1044 // absolute path version of it.
1045 if (FileInfo(fil).exist())
1046 return MakeAbsPath(fil);
1048 // No we try to find it using kpsewhich.
1049 // It seems from the kpsewhich manual page that it is safe to use
1050 // kpsewhich without --format: "When the --format option is not
1051 // given, the search path used when looking for a file is inferred
1052 // from the name given, by looking for a known extension. If no
1053 // known extension is found, the search path for TeX source files
1055 // However, we want to take advantage of the format sine almost all
1056 // the different formats has environment variables that can be used
1057 // to controll which paths to search. f.ex. bib looks in
1058 // BIBINPUTS and TEXBIB. Small list follows:
1059 // bib - BIBINPUTS, TEXBIB
1061 // graphic/figure - TEXPICTS, TEXINPUTS
1062 // ist - TEXINDEXSTYLE, INDEXSTYLE
1063 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1065 // tfm - TFMFONTS, TEXFONTS
1066 // This means that to use kpsewhich in the best possible way we
1067 // should help it by setting additional path in the approp. envir.var.
1068 string const kpsecmd = "kpsewhich " + fil;
1070 cmdret const c = do_popen(kpsecmd);
1072 lyxerr[Debug::LATEX] << "kpse status = " << c.first << "\n"
1073 << "kpse result = `" << strip(c.second, '\n')
1076 return strip(strip(c.second, '\n'), '\r');
1082 void removeAutosaveFile(string const & filename)
1084 string a = OnlyPath(filename);
1086 a += OnlyFilename(filename);
1088 FileInfo const fileinfo(a);
1089 if (fileinfo.exist()) {
1090 if (lyx::unlink(a) != 0) {
1091 WriteFSAlert(_("Could not delete auto-save file!"), a);