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>
62 extern string system_lyxdir;
63 extern string build_lyxdir;
64 extern string user_lyxdir;
65 extern string system_tempdir;
68 bool IsLyXFilename(string const & filename)
70 return suffixIs(filename, ".lyx");
74 bool IsSGMLFilename(string const & filename)
76 return suffixIs(filename, ".sgml");
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 // Is a file readable ?
116 bool IsFileReadable (string const & path)
119 if (file.isOK() && file.isRegular() && file.readable())
126 // Is a file read_only?
127 // return 1 read-write
129 // -1 error (doesn't exist, no access, anything else)
130 int IsFileWriteable (string const & path)
133 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
135 if (fi.readable()) // read-only
137 return -1; // everything else.
141 //returns 1: dir writeable
143 // -1: error- couldn't find out
144 int IsDirWriteable (string const & path)
146 string const tmpfl(lyx::tempName(path, "lyxwritetest"));
147 // We must unlink the tmpfl.
151 WriteFSAlert(_("LyX Internal Error!"),
152 _("Could not test if directory is writeable"));
156 if (fi.writable()) return 1;
162 // Uses a string of paths separated by ";"s to find a file to open.
163 // Can't cope with pathnames with a ';' in them. Returns full path to file.
164 // If path entry begins with $$LyX/, use system_lyxdir
165 // If path entry begins with $$User/, use user_lyxdir
166 // Example: "$$User/doc;$$LyX/doc"
167 string const FileOpenSearch (string const & path, string const & name,
170 string real_file, path_element;
171 bool notfound = true;
172 string tmppath = split(path, path_element, ';');
174 while (notfound && !path_element.empty()) {
175 path_element = CleanupPath(path_element);
176 if (!suffixIs(path_element, '/'))
178 path_element = subst(path_element, "$$LyX", system_lyxdir);
179 path_element = subst(path_element, "$$User", user_lyxdir);
181 real_file = FileSearch(path_element, name, ext);
183 if (real_file.empty()) {
185 tmppath = split(tmppath, path_element, ';');
186 } while(!tmppath.empty() && path_element.empty());
192 if (ext.empty() && notfound) {
193 real_file = FileOpenSearch(path, name, "exe");
194 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
201 /// Returns a vector of all files in directory dir having extension ext.
202 vector<string> const DirList(string const & dir, string const & ext)
204 // This is a non-error checking C/system implementation
205 string extension(ext);
206 if (!extension.empty() && extension[0] != '.')
207 extension.insert(0, ".");
208 vector<string> dirlist;
209 DIR * dirp = ::opendir(dir.c_str());
211 while ((dire = ::readdir(dirp))) {
212 string const fil = dire->d_name;
213 if (suffixIs(fil, extension)) {
214 dirlist.push_back(fil);
219 /* I would have prefered to take a vector<string>& as parameter so
220 that we could avoid the copy of the vector when returning.
222 dirlist.swap(argvec);
223 to avoid the copy. (Lgb)
225 /* A C++ implementaion will look like this:
226 string extension(ext);
227 if (extension[0] != '.') extension.insert(0, ".");
228 vector<string> dirlist;
229 directory_iterator dit("dir");
230 while (dit != directory_iterator()) {
231 string fil = (*dit).filename;
232 if (prefixIs(fil, extension)) {
233 dirlist.push_back(fil);
237 dirlist.swap(argvec);
243 // Returns the real name of file name in directory path, with optional
245 string const FileSearch(string const & path, string const & name,
248 // if `name' is an absolute path, we ignore the setting of `path'
249 // Expand Environmentvariables in 'name'
250 string const tmpname = ReplaceEnvironmentPath(name);
251 string fullname = MakeAbsPath(tmpname, path);
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 = subst(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() << "Deleting file: " << unlinkpath << endl;
417 if (FileInfo(unlinkpath).isDir())
418 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
419 deleted &= (lyx::unlink(unlinkpath) == 0);
421 WriteFSAlert (_("Error! Could not remove file:"),
431 string const CreateTmpDir(string const & tempdir, string const & mask)
434 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
435 << "CreateTmpDir: mask=`" << mask << "'" << endl;
437 string const tmpfl(lyx::tempName(tempdir, mask));
438 // lyx::tempName actually creates a file to make sure that it
439 // stays unique. So we have to delete it before we can create
440 // a dir with the same name. Note also that we are not thread
441 // safe because of the gap between unlink and mkdir. (Lgb)
442 lyx::unlink(tmpfl.c_str());
444 if (tmpfl.empty() || lyx::mkdir(tmpfl, 0700)) {
445 WriteFSAlert(_("Error! Couldn't create temporary directory:"),
449 return MakeAbsPath(tmpfl);
453 int DestroyTmpDir(string const & tmpdir, bool Allfiles)
458 if (Allfiles && DeleteAllFilesInDir(tmpdir)) return -1;
459 if (lyx::rmdir(tmpdir)) {
460 WriteFSAlert(_("Error! Couldn't delete temporary directory:"),
470 string const CreateBufferTmpDir(string const & pathfor)
472 return CreateTmpDir(pathfor, "lyx_tmpbuf");
476 int DestroyBufferTmpDir(string const & tmpdir)
478 return DestroyTmpDir(tmpdir, true);
482 string const CreateLyXTmpDir(string const & deflt)
484 if ((!deflt.empty()) && (deflt != "/tmp")) {
485 if (lyx::mkdir(deflt, 0777)) {
489 string const t(CreateTmpDir(deflt, "lyx_tmpdir"));
497 string const t(CreateTmpDir("/tmp", "lyx_tmpdir"));
503 int DestroyLyXTmpDir(string const & tmpdir)
505 return DestroyTmpDir (tmpdir, false); // Why false?
509 // Creates directory. Returns true if succesfull
510 bool createDirectory(string const & path, int permission)
512 string temp(strip(CleanupPath(path), '/'));
515 WriteAlert(_("Internal error!"),
516 _("Call to createDirectory with invalid name"));
520 if (lyx::mkdir(temp, permission)) {
521 WriteFSAlert (_("Error! Couldn't create directory:"), temp);
528 // Strip filename from path name
529 string const OnlyPath(string const & Filename)
531 // If empty filename, return empty
532 if (Filename.empty()) return Filename;
534 // Find last / or start of filename
535 string::size_type j = Filename.rfind('/');
536 if (j == string::npos)
538 return Filename.substr(0, j + 1);
542 // Convert relative path into absolute path based on a basepath.
543 // If relpath is absolute, just use that.
544 // If basepath is empty, use CWD as base.
545 string const MakeAbsPath(string const & RelPath, string const & BasePath)
547 // checks for already absolute path
548 if (AbsolutePath(RelPath))
550 if (RelPath[0]!= '/' && RelPath[0]!= '\\')
554 // Copies given paths
555 string TempRel(CleanupPath(RelPath));
559 if (!BasePath.empty()) {
563 char * with_drive = new char[_MAX_PATH];
564 _abspath(with_drive, BasePath.c_str(), _MAX_PATH);
565 TempBase = with_drive;
569 TempBase = lyx::getcwd(); //GetCWD();
571 if (AbsolutePath(TempRel))
572 return TempBase.substr(0, 2) + TempRel;
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);
604 // Add this piece to TempBase
605 if (!suffixIs(TempBase, '/'))
611 // returns absolute path
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 = CleanupPath(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() && (path[0] == '/' || (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 CleanupPath(string const & path)
731 #ifdef __EMX__ /* SMiyata: This should fix searchpath bug. */
732 string temppath = subst(path, '\\', '/');
733 temppath = subst(temppath, "//", "/");
734 return lowercase(temppath);
735 #else // On unix, nothing to do
741 string const GetFileContents(string const & fname)
743 FileInfo finfo(fname);
745 ifstream ifs(fname.c_str());
746 std::ostringstream ofs;
750 return ofs.str().c_str();
753 lyxerr << "LyX was not able to read file '" << fname << "'" << endl;
759 // Search ${...} as Variable-Name inside the string and replace it with
760 // the denoted environmentvariable
761 // Allow Variables according to
762 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
765 string const ReplaceEnvironmentPath(string const & path)
768 // CompareChar: Environmentvariables starts with this character
769 // PathChar: Next path component start with this character
770 // while CompareChar found do:
771 // Split String with PathChar
772 // Search Environmentvariable
773 // if found: Replace Strings
775 char const CompareChar = '$';
776 char const FirstChar = '{';
777 char const EndChar = '}';
778 char const UnderscoreChar = '_';
779 string EndString; EndString += EndChar;
780 string FirstString; FirstString += FirstChar;
781 string CompareString; CompareString += CompareChar;
782 string const RegExp("*}*"); // Exist EndChar inside a String?
784 // first: Search for a '$' - Sign.
786 string result1; //(copy); // for split-calls
787 string result0 = split(path, result1, CompareChar);
788 while (!result0.empty()) {
789 string copy1(result0); // contains String after $
791 // Check, if there is an EndChar inside original String.
793 if (!regexMatch(copy1, RegExp)) {
794 // No EndChar inside. So we are finished
795 result1 += CompareString + result0;
801 string res0 = split(copy1, res1, EndChar);
802 // Now res1 holds the environmentvariable
803 // First, check, if Contents is ok.
804 if (res1.empty()) { // No environmentvariable. Continue Loop.
805 result1 += CompareString + FirstString;
809 // check contents of res1
810 char const * res1_contents = res1.c_str();
811 if (*res1_contents != FirstChar) {
812 // Again No Environmentvariable
813 result1 += CompareString;
817 // Check for variable names
818 // Situation ${} is detected as "No Environmentvariable"
819 char const * cp1 = res1_contents + 1;
820 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
822 while (*cp1 && result) {
823 result = isalnum(*cp1) ||
824 (*cp1 == UnderscoreChar);
829 // no correct variable name
830 result1 += CompareString + res1 + EndString;
831 result0 = split(res0, res1, CompareChar);
836 string env(GetEnv(res1_contents + 1));
838 // Congratulations. Environmentvariable found
841 result1 += CompareString + res1 + EndString;
844 result0 = split(res0, res1, CompareChar);
848 } // ReplaceEnvironmentPath
851 // Make relative path out of two absolute paths
852 string const MakeRelPath(string const & abspath0, string const & basepath0)
853 // Makes relative path out of absolute path. If it is deeper than basepath,
854 // it's easy. If basepath and abspath share something (they are all deeper
855 // than some directory), it'll be rendered using ..'s. If they are completely
856 // different, then the absolute path will be used as relative path.
858 // This is a hack. It should probaly be done in another way. Lgb.
859 string const abspath = CleanupPath(abspath0);
860 string const basepath = CleanupPath(basepath0);
862 return "<unknown_path>";
864 string::size_type const abslen = abspath.length();
865 string::size_type const baselen = basepath.length();
867 // Find first different character
868 string::size_type i = 0;
869 while (i < abslen && i < baselen && abspath[i] == basepath[i]) ++i;
872 if (i < abslen && i < baselen
873 || (i < abslen && abspath[i] != '/' && i == baselen)
874 || (i < baselen && basepath[i] != '/' && i == abslen))
876 if (i) --i; // here was the last match
877 while (i && abspath[i] != '/') --i;
881 // actually no match - cannot make it relative
885 // Count how many dirs there are in basepath above match
886 // and append as many '..''s into relpath
888 string::size_type j = i;
889 while (j < baselen) {
890 if (basepath[j] == '/') {
891 if (j + 1 == baselen) break;
897 // Append relative stuff from common directory to abspath
898 if (abspath[i] == '/') ++i;
899 for (; i < abslen; ++i)
902 if (suffixIs(buf, '/'))
903 buf.erase(buf.length() - 1);
904 // Substitute empty with .
911 // Append sub-directory(ies) to a path in an intelligent way
912 string const AddPath(string const & path, string const & path_2)
915 string const path2 = CleanupPath(path_2);
917 if (!path.empty() && path != "." && path != "./") {
918 buf = CleanupPath(path);
919 if (path[path.length() - 1] != '/')
924 buf += frontStrip(strip(path2, '/'), '/') + '/';
931 Change extension of oldname to extension.
932 Strips path off if no_path == true.
933 If no extension on oldname, just appends.
936 ChangeExtension(string const & oldname, string const & extension)
938 string::size_type const last_slash = oldname.rfind('/');
939 string::size_type last_dot = oldname.rfind('.');
940 if (last_dot < last_slash && last_slash != string::npos)
941 last_dot = string::npos;
944 // Make sure the extension starts with a dot
945 if (!extension.empty() && extension[0] != '.')
946 ext= "." + extension;
950 return CleanupPath(oldname.substr(0, last_dot) + ext);
954 /// Return the extension of the file (not including the .)
955 string const GetExtension(string const & name)
957 string::size_type const last_slash = name.rfind('/');
958 string::size_type const last_dot = name.rfind('.');
959 if (last_dot != string::npos &&
960 (last_slash == string::npos || last_dot > last_slash))
961 return name.substr(last_dot + 1,
962 name.length() - (last_dot + 1));
968 // Creates a nice compact path for displaying
970 MakeDisplayPath (string const & path, unsigned int threshold)
972 string::size_type const l1 = path.length();
974 // First, we try a relative path compared to home
975 string const home(GetEnvPath("HOME"));
976 string relhome = MakeRelPath(path, home);
978 string::size_type l2 = relhome.length();
982 // If we backup from home or don't have a relative path,
983 // this try is no good
984 if (prefixIs(relhome, "../") || AbsolutePath(relhome)) {
985 // relative path was no good, just use the original path
992 // Is the path too long?
993 if (l2 > threshold) {
999 while (relhome.length() > threshold)
1000 relhome = split(relhome, temp, '/');
1002 // Did we shortend everything away?
1003 if (relhome.empty()) {
1004 // Yes, filename in itself is too long.
1005 // Pick the start and the end of the filename.
1006 relhome = OnlyFilename(path);
1007 string const head = relhome.substr(0, threshold/2 - 3);
1009 l2 = relhome.length();
1011 relhome.substr(l2 - threshold/2 - 2, l2 - 1);
1012 relhome = head + "..." + tail;
1015 return prefix + relhome;
1019 bool LyXReadLink(string const & File, string & Link)
1021 char LinkBuffer[512];
1022 // Should be PATH_MAX but that needs autconf support
1023 int const nRead = ::readlink(File.c_str(),
1024 LinkBuffer, sizeof(LinkBuffer) - 1);
1027 LinkBuffer[nRead] = '\0'; // terminator
1035 typedef pair<int, string> cmdret;
1037 cmdret const do_popen(string const & cmd)
1039 // One question is if we should use popen or
1040 // create our own popen based on fork, exec, pipe
1041 // of course the best would be to have a
1042 // pstream (process stream), with the
1043 // variants ipstream, opstream
1044 FILE * inf = ::popen(cmd.c_str(), "r");
1048 ret += static_cast<char>(c);
1051 int const pret = pclose(inf);
1052 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 const fileinfo(a);
1113 if (fileinfo.exist()) {
1114 if (lyx::unlink(a) != 0) {
1115 WriteFSAlert(_("Could not delete auto-save file!"), a);