]> git.lyx.org Git - lyx.git/blobdiff - src/support/filetools.C
the freespacing patch from Kayvan, draw the math empty delim with onoffdash, asure...
[lyx.git] / src / support / filetools.C
index fe7c3d456cd7ca025fad31f1c94361c01a4814da..1efd55bffe87b1cc63660ee5568433f89cac0ab6 100644 (file)
 
 #include <cctype>
 
+#include <utility>
+using std::make_pair;
+using std::pair;
+
 #ifdef __GNUG__
 #pragma implementation "filetools.h"
 #endif
 
 #include "filetools.h"
+#include "LSubstring.h"
 #include "lyx_gui_misc.h"
 #include "FileInfo.h"
 #include "support/path.h"        // I know it's OS/2 specific (SMiyata)
 #include "gettext.h"
-#include "LAssert.h"
 #include "lyxlib.h"
 
 // Which part of this is still necessary? (JMarc).
@@ -67,22 +71,38 @@ bool IsSGMLFilename(string const & filename)
 
 
 // Substitutes spaces with underscores in filename (and path)
-string SpaceLess(string const & file)
+string MakeLatexName(string const & file)
 {
        string name = OnlyFilename(file);
        string path = OnlyPath(file);
        
        for (string::size_type i = 0; i < name.length(); ++i) {
-               name[i] &= 0x7f;
-               if (!isalnum(name[i]) && name[i] != '.')
-                       name[i] = '_';
+               name[i] &= 0x7f; // set 8th bit to 0
+       };
+
+       // ok so we scan through the string twice, but who cares.
+       string keep("abcdefghijklmnopqrstuvwxyz"
+               "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+               "@!\"'()*+,-./0123456789:;<=>?[]`|");
+       
+       string::size_type pos = 0;
+       while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
+               name[pos++] = '_';
        }
-       string temp = AddName(path, name);
-       // Replace spaces with underscores, also in directory
-       // No!!! I checked it that it is not necessary.
-       // temp = subst(temp, ' ', '_');
+       return AddName(path, name);
+}
 
-       return temp;
+// Substitutes spaces with underscores in filename (and path)
+string QuoteName(string const & name)
+{
+#ifdef WITH_WARNINGS
+#warning Add proper emx support here!
+#endif
+#ifndef __EMX__
+       return '\'' + name + '\'';
+#else
+       return name; 
+#endif
 }
 
 
@@ -102,9 +122,9 @@ string TmpFileName(string const & dir, string const & mask)
        // a short string...
        string ret;
        FileInfo fnfo;
-       for (int a='a'; a<= 'z'; ++a)
-               for (int b='a'; b<= 'z'; ++b)
-                       for (int c='a'; c<= 'z'; ++c) {
+       for (int a = 'a'; a <= 'z'; ++a)
+               for (int b = 'a'; b <= 'z'; ++b)
+                       for (int c = 'a'; c <= 'z'; ++c) {
                                // if this is not enough I have no idea what
                                // to do.
                                ret = tmpfl + char(a) + char(b) + char(c);
@@ -134,18 +154,12 @@ bool IsFileReadable (string const & path)
 //      -1 error (doesn't exist, no access, anything else) 
 int IsFileWriteable (string const & path)
 {
-       FilePtr fp(path, FilePtr::update);
-       if (!fp()) {
-               if ((errno == EACCES) || (errno == EROFS)) {
-                       //fp = FilePtr(path, FilePtr::read);
-                       fp.reopen(path, FilePtr::read);
-                       if (fp()) {
-                               return 0;
-                       }
-               }
-               return -1;
-       }
-       return 1;
+       FileInfo fi(path);
+       if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
+               return 1;
+       if (fi.readable()) // read-only
+               return 0;
+       return -1; // everything else.
 }
 
 
@@ -161,23 +175,10 @@ int IsDirWriteable (string const & path)
                             _("Could not test if directory is writeable"));
                return -1;
        } else {
-       FilePtr fp(tmpfl, FilePtr::truncate);
-       if (!fp()) {
-               if (errno == EACCES) {
-                       return 0;
-               } else { 
-                       WriteFSAlert(_("LyX Internal Error!"), 
-                                    _("Cannot open directory test file"));
-                       return -1;
-               }
-               }
+               FileInfo fi(path);
+               if (fi.writable()) return 1;
+               return 0;
        }
-               if (remove (tmpfl.c_str())) {
-                       WriteFSAlert(_("LyX Internal Error!"), 
-                                   _("Created test file but cannot remove it?"));
-                       return -1;
-       }
-       return 1;
 }
 
 
@@ -187,27 +188,27 @@ int IsDirWriteable (string const & path)
 // If path entry begins with $$User/, use user_lyxdir
 // Example: "$$User/doc;$$LyX/doc"
 string FileOpenSearch (string const & path, string const & name, 
-                       string const & ext)
+                      string const & ext)
 {
        string real_file, path_element;
        bool notfound = true;
-       string tmppath=split(path, path_element, ';');
+       string tmppath = split(path, path_element, ';');
        
        while (notfound && !path_element.empty()) {
                path_element = CleanupPath(path_element);
                if (!suffixIs(path_element, '/'))
-                       path_element+='/';
+                       path_element+= '/';
                path_element = subst(path_element, "$$LyX", system_lyxdir);
                path_element = subst(path_element, "$$User", user_lyxdir);
                
                real_file = FileSearch(path_element, name, ext);
-
+               
                if (real_file.empty()) {
-                 do {
-                   tmppath = split(tmppath, path_element, ';');
-                 } while(!tmppath.empty() && path_element.empty());
+                       do {
+                               tmppath = split(tmppath, path_element, ';');
+                       } while(!tmppath.empty() && path_element.empty());
                } else {
-                 notfound = false;
+                       notfound = false;
                }
        }
 #ifdef __EMX__
@@ -251,33 +252,33 @@ string FileSearch(string const & path, string const & name,
 //   2) build_lyxdir (if not empty)
 //   3) system_lyxdir
 string LibFileSearch(string const & dir, string const & name, 
-                     string const & ext)
+                    string const & ext)
 {
-        string fullname = FileSearch(AddPath(user_lyxdir,dir), name,
-                                     ext); 
+        string fullname = FileSearch(AddPath(user_lyxdir, dir),
+                                    name, ext); 
        if (!fullname.empty())
                return fullname;
-
+       
        if (!build_lyxdir.empty()) 
                fullname = FileSearch(AddPath(build_lyxdir, dir), 
                                      name, ext);
        if (!fullname.empty())
                return fullname;
-
-       return FileSearch(AddPath(system_lyxdir,dir), name, ext);
+       
+       return FileSearch(AddPath(system_lyxdir, dir), name, ext);
 }
 
 
 string i18nLibFileSearch(string const & dir, string const & name, 
-                         string const & ext)
+                        string const & ext)
 {
        string lang = token(string(GetEnv("LANG")), '_', 0);
-
+       
        if (lang.empty() || lang == "C")
                return LibFileSearch(dir, name, ext);
        else {
                string tmp = LibFileSearch(dir, lang + '_' + name,
-                                           ext);
+                                          ext);
                if (!tmp.empty())
                        return tmp;
                else
@@ -312,17 +313,33 @@ bool PutEnv(string const & envstr)
 #warning Look at and fix this.
 #endif
         // f.ex. what about error checking?
-        int retval = 0;
 #if HAVE_PUTENV
         // this leaks, but what can we do about it?
         //   Is doing a getenv() and a free() of the older value 
         //   a good idea? (JMarc)
-        retval = putenv((new string(envstr))->c_str());
+       // Actually we don't have to leak...calling putenv like this
+       // should be enough: ... and this is obviously not enough if putenv
+       // does not make a copy of the string. It is also not very wise to
+       // put a string on the free store. If we have to leak we should do it
+       // like this:
+       char * leaker = new char[envstr.length() + 1];
+       envstr.copy(leaker, envstr.length());
+       leaker[envstr.length()] = '\0';
+       int retval = lyx::putenv(leaker);
+
+       // If putenv does not make a copy of the char const * this
+       // is very dangerous. OTOH if it does take a copy this is the
+       // best solution.
+       // The  only implementation of putenv that I have seen does not
+       // allocate memory. _And_ after testing the putenv in glibc it
+       // seems that we need to make a copy of the string contents.
+       // I will enable the above.
+       //int retval = lyx::putenv(envstr.c_str());
 #else
 #ifdef HAVE_SETENV 
         string varname;
         string str = envstr.split(varname,'=');
-        retval = setenv(varname.c_str(), str.c_str(), true);
+        int retval = setenv(varname.c_str(), str.c_str(), true);
 #endif
 #endif
         return retval == 0;
@@ -338,26 +355,43 @@ bool PutEnvPath(string const & envstr)
 static
 int DeleteAllFilesInDir (string const & path)
 {
-       DIR * dir;
-       struct dirent * de;
-       dir = opendir(path.c_str());
+       // I have decided that we will be using parts from the boost
+       // library. Check out http://www.boost.org/
+       // For directory access we will then use the directory_iterator.
+       // Then the code will be something like:
+       // directory_iterator dit(path.c_str());
+       // if (<some way to detect failure>) {
+       //         WriteFSAlert(_("Error! Cannot open directory:"), path);
+       //         return -1;
+       // }
+       // for (; dit != <someend>; ++dit) {
+       //         if ((*dit) == 2." || (*dit) == "..")
+       //                 continue;
+       //         string unlinkpath = AddName(path, temp);
+       //         if (remove(unlinkpath.c_str()))
+       //                 WriteFSAlert(_("Error! Could not remove file:"),
+       //                              unlinkpath);
+       // }
+       // return 0;
+       DIR * dir = opendir(path.c_str());
        if (!dir) {
                WriteFSAlert (_("Error! Cannot open directory:"), path);
                return -1;
        }
+       struct dirent * de;
        while ((de = readdir(dir))) {
                string temp = de->d_name;
-               if (temp=="." || temp=="..") 
+               if (temp == "." || temp == "..") 
                        continue;
                string unlinkpath = AddName (path, temp);
 
                lyxerr.debug() << "Deleting file: " << unlinkpath << endl;
 
-               if (remove (unlinkpath.c_str()))
+               if (remove(unlinkpath.c_str()))
                        WriteFSAlert (_("Error! Could not remove file:"), 
                                      unlinkpath);
         }
-       closedir (dir);
+       closedir(dir);
        return 0;
 }
 
@@ -367,7 +401,7 @@ string CreateTmpDir (string const & tempdir, string const & mask)
 {
        string tmpfl = TmpFileName(tempdir, mask);
        
-       if ((tmpfl.empty()) || mkdir (tmpfl.c_str(), 0777)) {
+       if ((tmpfl.empty()) || lyx::mkdir (tmpfl.c_str(), 0777)) {
                WriteFSAlert(_("Error! Couldn't create temporary directory:"),
                             tempdir);
                return string();
@@ -394,26 +428,24 @@ int DestroyTmpDir (string const & tmpdir, bool Allfiles)
 
 string CreateBufferTmpDir (string const & pathfor)
 {
-       return CreateTmpDir (pathfor, "lyx_bufrtmp");
+       return CreateTmpDir(pathfor, "lyx_bufrtmp");
 }
 
 
 int DestroyBufferTmpDir (string const & tmpdir)
 {
-       return DestroyTmpDir (tmpdir, true);
+       return DestroyTmpDir(tmpdir, true);
 }
 
 
 string CreateLyXTmpDir (string const & deflt)
 {
-       string t;        
-
        if ((!deflt.empty()) && (deflt  != "/tmp")) {
-               if (mkdir (deflt.c_str(), 0777)) {
+               if (lyx::mkdir(deflt.c_str(), 0777)) {
 #ifdef __EMX__
                         Path p(user_lyxdir);
 #endif
-                       t = CreateTmpDir (deflt.c_str(), "lyx_tmp");
+                       string t = CreateTmpDir (deflt.c_str(), "lyx_tmp");
                         return t;
                } else
                         return deflt;
@@ -421,7 +453,7 @@ string CreateLyXTmpDir (string const & deflt)
 #ifdef __EMX__
                Path p(user_lyxdir);
 #endif
-               t = CreateTmpDir ("/tmp", "lyx_tmp");
+               string t = CreateTmpDir ("/tmp", "lyx_tmp");
                return t;
        }
 }
@@ -444,7 +476,7 @@ bool createDirectory(string const & path, int permission)
                return false;
        }
 
-       if (mkdir(temp.c_str(), permission)) {
+       if (lyx::mkdir(temp.c_str(), permission)) {
                WriteFSAlert (_("Error! Couldn't create directory:"), temp);
                return false;
        }
@@ -458,7 +490,6 @@ string GetCWD ()
        int n = 256;    // Assume path is less than 256 chars
        char * err;
        char * tbuf = new char[n];
-       string result;
        
        // Safe. Hopefully all getcwds behave this way!
        while (((err = lyx::getcwd (tbuf, n)) == 0) && (errno == ERANGE)) {
@@ -468,6 +499,7 @@ string GetCWD ()
                tbuf = new char[n];
        }
 
+       string result;
        if (err) result = tbuf;
        delete[] tbuf;
        return result;
@@ -496,7 +528,7 @@ string MakeAbsPath(string const & RelPath, string const & BasePath)
        // checks for already absolute path
        if (AbsolutePath(RelPath))
 #ifdef __EMX__
-               if(RelPath[0]!='/' && RelPath[0]!='\\')
+               if(RelPath[0]!= '/' && RelPath[0]!= '\\')
 #endif
                return RelPath;
 
@@ -533,10 +565,10 @@ string MakeAbsPath(string const & RelPath, string const & BasePath)
                // Split by next /
                RTemp = split(RTemp, Temp, '/');
                
-               if (Temp==".") continue;
-               if (Temp=="..") {
+               if (Temp == ".") continue;
+               if (Temp == "..") {
                        // Remove one level of TempBase
-                       int i = TempBase.length()-2;
+                       int i = TempBase.length() - 2;
 #ifndef __EMX__
                        if (i < 0) i = 0;
                        while (i > 0 && TempBase[i] != '/') --i;
@@ -558,7 +590,7 @@ string MakeAbsPath(string const & RelPath, string const & BasePath)
        }
 
        // returns absolute path
-       return TempBase;        
+       return TempBase;
 }
 
 
@@ -603,7 +635,7 @@ bool AbsolutePath(string const & path)
 #ifndef __EMX__
        return (!path.empty() && path[0] == '/');
 #else
-       return (!path.empty() && (path[0]=='/' || (isalpha((unsigned char) path[0]) && path.length()>1 && path[1]==':')));
+       return (!path.empty() && (path[0] == '/' || (isalpha(static_cast<unsigned char>(path[0])) && path.length()>1 && path[1] == ':')));
 #endif
 }
 
@@ -612,7 +644,6 @@ bool AbsolutePath(string const & path)
 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
 string ExpandPath(string const & path)
 {
-       Assert(!path.empty()); // We don't allow empty path. (Lgb)
        // checks for already absolute path
        string RTemp = ReplaceEnvironmentPath(path);
        if (AbsolutePath(RTemp))
@@ -622,13 +653,13 @@ string ExpandPath(string const & path)
        string copy(RTemp);
 
        // Split by next /
-       RTemp=split(RTemp, Temp, '/');
+       RTemp= split(RTemp, Temp, '/');
 
-       if (Temp==".") {
+       if (Temp == ".") {
                return GetCWD() + '/' + RTemp;
-       } else if (Temp=="~") {
+       } else if (Temp == "~") {
                return GetEnvPath("HOME") + '/' + RTemp;
-       } else if (Temp=="..") {
+       } else if (Temp == "..") {
                return MakeAbsPath(copy);
        } else
                // Don't know how to handle this
@@ -655,15 +686,15 @@ string NormalizePath(string const & path)
                // Split by next /
                RTemp = split(RTemp, Temp, '/');
                
-               if (Temp==".") {
+               if (Temp == ".") {
                        TempBase = "./";
-               } else if (Temp=="..") {
+               } else if (Temp == "..") {
                        // Remove one level of TempBase
-                       int i = TempBase.length()-2;
-                       while (i>0 && TempBase[i] != '/')
+                       int i = TempBase.length() - 2;
+                       while (i > 0 && TempBase[i] != '/')
                                --i;
-                       if (i>=0 && TempBase[i] == '/')
-                               TempBase.erase(i+1, string::npos);
+                       if (i >= 0 && TempBase[i] == '/')
+                               TempBase.erase(i + 1, string::npos);
                        else
                                TempBase = "../";
                } else {
@@ -696,7 +727,6 @@ string CleanupPath(string const & path)
 
 string ReplaceEnvironmentPath(string const & path)
 {
-       Assert(!path.empty()); // We don't allow empty path. (Lgb)
 // 
 // CompareChar: Environmentvariables starts with this character
 // PathChar:    Next path component start with this character
@@ -705,14 +735,14 @@ string ReplaceEnvironmentPath(string const & path)
 //      Search Environmentvariable
 //      if found: Replace Strings
 //
-       const char CompareChar = '$';
-       const char FirstChar = '{'; 
-       const char EndChar = '}'; 
-       const char UnderscoreChar = '_'; 
+       char const CompareChar = '$';
+       char const FirstChar = '{'; 
+       char const EndChar = '}'; 
+       char const UnderscoreChar = '_'; 
        string EndString; EndString += EndChar;
        string FirstString; FirstString += FirstChar;
        string CompareString; CompareString += CompareChar;
-       const string RegExp("*}*"); // Exist EndChar inside a String?
+       string const RegExp("*}*"); // Exist EndChar inside a String?
 
 // first: Search for a '$' - Sign.
        //string copy(path);
@@ -740,20 +770,20 @@ string ReplaceEnvironmentPath(string const & path)
                        continue;
                }
                // check contents of res1
-               const char * res1_contents = res1.c_str();
+               char const * res1_contents = res1.c_str();
                if (*res1_contents != FirstChar) {
                        // Again No Environmentvariable
                        result1 += CompareString;
-                       result0  = res0;
+                       result0 = res0;
                }
 
                // Check for variable names
                // Situation ${} is detected as "No Environmentvariable"
-               const char * cp1 = res1_contents+1;
-               bool result = isalpha((unsigned char) *cp1) || (*cp1 == UnderscoreChar);
+               char const * cp1 = res1_contents + 1;
+               bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
                ++cp1;
                while (*cp1 && result) {
-                       result = isalnum((unsigned char) *cp1) || 
+                       result = isalnum(*cp1) || 
                                (*cp1 == UnderscoreChar); 
                        ++cp1;
                }
@@ -803,8 +833,8 @@ string MakeRelPath(string const & abspath0, string const & basepath0)
 
        // Go back to last /
        if (i < abslen && i < baselen
-           || (i<abslen && abspath[i] != '/' && i==baselen)
-           || (i<baselen && basepath[i] != '/' && i==abslen))
+           || (i<abslen && abspath[i] != '/' && i == baselen)
+           || (i<baselen && basepath[i] != '/' && i == abslen))
        {
                if (i) --i;     // here was the last match
                while (i && abspath[i] != '/') --i;
@@ -821,7 +851,7 @@ string MakeRelPath(string const & abspath0, string const & basepath0)
        int j = i;
        while (j < baselen) {
                if (basepath[j] == '/') {
-                       if (j+1 == baselen) break;
+                       if (j + 1 == baselen) break;
                        buf += "../";
                }
                ++j;
@@ -829,7 +859,7 @@ string MakeRelPath(string const & abspath0, string const & basepath0)
 
        // Append relative stuff from common directory to abspath
        if (abspath[i] == '/') ++i;
-       for (; i<abslen; ++i)
+       for (; i < abslen; ++i)
                buf += abspath[i];
        // Remove trailing /
        if (suffixIs(buf, '/'))
@@ -850,16 +880,13 @@ string AddPath(string const & path, string const & path_2)
        if (!path.empty() && path != "." && path != "./") {
                buf = CleanupPath(path);
                if (path[path.length() - 1] != '/')
-                                                          
                        buf += '/';
        }
 
        if (!path2.empty()){
                int p2start = path2.find_first_not_of('/');
-               //while (path2[p2start] == '/') ++p2start;
 
                int p2end = path2.find_last_not_of('/');
-               //while (path2[p2end] == '/') --p2end;
 
                string tmp = path2.substr(p2start, p2end - p2start + 1);
                buf += tmp + '/';
@@ -877,16 +904,14 @@ string ChangeExtension(string const & oldname, string const & extension,
                        bool no_path) 
 {
        string::size_type last_slash = oldname.rfind('/');
-       string::size_type last_dot;
-       if (last_slash != string::npos)
-               last_dot = oldname.find('.', last_slash);
-       else
-               last_dot = oldname.rfind('.');
-
+       string::size_type last_dot = oldname.rfind('.');
+       if (last_dot < last_slash && last_slash != string::npos)
+               last_dot = string::npos;
+       
        string ext;
        // Make sure the extension starts with a dot
        if (!extension.empty() && extension[0] != '.')
-               ext='.' + extension;
+               ext= '.' + extension;
        else
                ext = extension;
        string ret_str;
@@ -900,7 +925,6 @@ string ChangeExtension(string const & oldname, string const & extension,
 }
 
 
-
 // Creates a nice compact path for displaying
 string MakeDisplayPath (string const & path, unsigned int threshold)
 {
@@ -950,15 +974,83 @@ string MakeDisplayPath (string const & path, unsigned int threshold)
        return prefix + relhome;
 }
 
+
 bool LyXReadLink(string const & File, string & Link)
 {
        char LinkBuffer[512];
-                // Should be PATH_MAX but that needs autconf support
-       int nRead;
-       nRead = readlink(File.c_str(), LinkBuffer,sizeof(LinkBuffer)-1);
+       // Should be PATH_MAX but that needs autconf support
+       int nRead = readlink(File.c_str(), LinkBuffer, sizeof(LinkBuffer)-1);
        if (nRead <= 0)
                return false;
        LinkBuffer[nRead] = 0;
        Link = LinkBuffer;
        return true;
 }
+
+
+typedef pair<int, string> cmdret;
+static
+cmdret do_popen(string const & cmd)
+{
+       // One question is if we should use popen or
+       // create our own popen based on fork, exec, pipe
+       // of course the best would be to have a
+       // pstream (process stream), with the
+       // variants ipstream, opstream
+       FILE * inf = popen(cmd.c_str(), "r");
+       string ret;
+       int c = fgetc(inf);
+       while (c != EOF) {
+               ret += static_cast<char>(c);
+               c = fgetc(inf);
+       }
+       int pret = pclose(inf);
+       return make_pair(pret, ret);
+}
+
+
+string findtexfile(string const & fil, string const & /*format*/)
+{
+       /* There is no problem to extend this function too use other
+          methods to look for files. It could be setup to look
+          in environment paths and also if wanted as a last resort
+          to a recursive find. One of the easier extensions would
+          perhaps be to use the LyX file lookup methods. But! I am
+          going to implement this until I see some demand for it.
+          Lgb
+       */
+       
+       // If the file can be found directly, we just return a
+       // absolute path version of it. 
+        if (FileInfo(fil).exist())
+               return MakeAbsPath(fil);
+
+        // No we try to find it using kpsewhich.
+       // It seems from the kpsewhich manual page that it is safe to use
+       // kpsewhich without --format: "When the --format option is not
+       // given, the search path used when looking for a file is inferred
+       // from the name given, by looking for a known extension. If no
+       // known extension is found, the search path for TeX source files
+       // is used."
+       // However, we want to take advantage of the format sine almost all
+       // the different formats has environment variables that can be used
+       // to controll which paths to search. f.ex. bib looks in
+       // BIBINPUTS and TEXBIB. Small list follows:
+       // bib - BIBINPUTS, TEXBIB
+       // bst - BSTINPUTS
+       // graphic/figure - TEXPICTS, TEXINPUTS
+       // ist - TEXINDEXSTYLE, INDEXSTYLE
+       // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
+       // tex - TEXINPUTS
+       // tfm - TFMFONTS, TEXFONTS
+       // This means that to use kpsewhich in the best possible way we
+       // should help it by setting additional path in the approp. envir.var.
+        string kpsecmd = "kpsewhich " + fil;
+
+        cmdret c = do_popen(kpsecmd);
+       
+        lyxerr[Debug::LATEX] << "kpse status = " << c.first << "\n"
+                            << "kpse result = `" << strip(c.second, '\n') 
+                            << "'" << endl;
+        return c.first != -1 ? strip(c.second, '\n') : string();
+}