Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members

vul_expand_path.cxx

Go to the documentation of this file.
00001 // This is core/vul/vul_expand_path.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 // \author fsm
00008 
00009 #include "vul_expand_path.h"
00010 #include <vcl_vector.h>
00011 
00012 #if defined(VCL_WIN32) || defined(como4301)
00013 
00014 //:
00015 // \note This Windows version only performs some of the operations done by the Unix version.
00016 vcl_string vul_expand_path_internal(vcl_string path)
00017 {
00018   if (path == "/")
00019     return path; // FIXME: without this something breaks; not sure why.
00020 
00021   { // main processing and reduction goes here.
00022     vcl_vector<vcl_string> bits;
00023 
00024     // split the path into bits. a "bit" is either a single slash or a
00025     // sequence of non-slash characters.
00026     for (unsigned int i=0; i<path.size(); ) {
00027       if (path[i] == '/') {
00028         bits.push_back("/");
00029         ++i;
00030       }
00031       else {
00032         unsigned int j=i;
00033         while (j<path.size() && path[j]!='/')
00034           ++j;
00035         bits.push_back(vcl_string(path.c_str()+i, path.c_str()+j));
00036         i = j;
00037       }
00038     }
00039 
00040     // process the bits
00041     while (true)
00042     {
00043       bool again = false;
00044       for (unsigned int i=0; i<bits.size(); ++i)
00045       {
00046         // remove repeated /
00047         if (i+1<bits.size() && bits[i] == "/" && bits[i+1] == "/") {
00048           bits.erase(bits.begin() + i);
00049           again = true;
00050         }
00051 
00052         // remove trailing /
00053         if (i+1 == bits.size() && bits[i] == "/") {
00054           bits.pop_back();
00055           again = true;
00056         }
00057 
00058         // collapse foo/.. into /
00059         if (i+2<bits.size() && !(bits[i]=="/") && bits[i+1]=="/" && bits[i+2]=="..") {
00060           bits.erase(bits.begin() + i+2); // ..
00061           bits.erase(bits.begin() + i);   // foo
00062           again = true;
00063         }
00064 
00065         // remove /. altogether
00066         if (i+1<bits.size() && bits[i]=="/" && bits[i+1]==".") {
00067           bits.erase(bits.begin() + i+1); // /
00068           bits.erase(bits.begin() + i);   // .
00069           again = true;
00070         }
00071       }
00072       if (!again)
00073         break;
00074     }
00075 
00076     // recompose the path from its bits
00077     path = "";
00078     for (unsigned int i=0; i<bits.size(); ++i)
00079       path += bits[i];
00080 #ifdef DEBUG
00081     vcl_cerr << "recomposed : " << path << '\n';
00082 #endif
00083   }
00084 
00085   // no more ideas
00086   return path;
00087 }
00088 
00089 //:
00090 // Note: this Windows version in similar to the uncached Unix version
00091 vcl_string vul_expand_path(vcl_string path)
00092 {
00093   return vul_expand_path_internal(path);
00094 }
00095 
00096 #else // #if defined(VCL_WIN32) || defined(como4301)
00097 
00098 #include <vcl_functional.h>
00099 #include <vcl_map.h>
00100 #include <vcl_cstdlib.h> // for getenv()
00101 #include <sys/types.h>
00102 #include <sys/stat.h>
00103 #include <dirent.h>
00104 #include <unistd.h>
00105 
00106 static
00107 vcl_string vul_expand_path_internal(vcl_string path)
00108 {
00109   if (path == "/")
00110     return path; // FIXME: without this something breaks; not sure why.
00111 
00112   // expand ~/ or just ~
00113   if ((path.size()>=2 && path[0] == '~' && path[1] == '/') || path == "~") {
00114     char const *HOME = vcl_getenv("HOME");
00115     if (! HOME) {
00116       // urgh!
00117       HOME = "/HOME";
00118     }
00119     path = vcl_string(HOME) + vcl_string(path.c_str() + 1);
00120   }
00121 
00122   // if the path doesn't begin with a / then it must be relative to the
00123   // current directory.
00124   if (path.size()>=1 && path[0] != '/')
00125     path = vcl_string("./") + path;
00126 
00127   // expand ./ or just .
00128   if ((path.size()>=2 && path[0] == '.' && path[1] == '/') || path == ".") {
00129     char cwd[4096];
00130     getcwd(cwd, sizeof cwd);
00131     path = vcl_string(cwd) + vcl_string(path.c_str() + 1);
00132   }
00133 
00134   { // main processing and reduction goes here.
00135     vcl_vector<vcl_string> bits;
00136 
00137     // split the path into bits. a "bit" is either a single slash or a
00138     // sequence of non-slash characters.
00139     for (unsigned int i=0; i<path.size(); ) {
00140       if (path[i] == '/') {
00141         bits.push_back("/");
00142         ++i;
00143       }
00144       else {
00145         unsigned int j=i;
00146         while (j<path.size() && path[j]!='/')
00147           ++j;
00148         bits.push_back(vcl_string(path.c_str()+i, path.c_str()+j));
00149         i = j;
00150       }
00151     }
00152 
00153     // process the bits
00154     while (true)
00155     {
00156       bool again = false;
00157       for (unsigned int i=0; i<bits.size(); ++i)
00158       {
00159         // remove repeated /
00160         if (i+1<bits.size() && bits[i] == "/" && bits[i+1] == "/") {
00161           bits.erase(bits.begin() + i);
00162           again = true;
00163         }
00164 
00165         // remove trailing /
00166         if (i+1 == bits.size() && bits[i] == "/") {
00167           bits.pop_back();
00168           again = true;
00169         }
00170 
00171         // collapse foo/.. into /
00172         if (i+2<bits.size() && !(bits[i]=="/") && bits[i+1]=="/" && bits[i+2]=="..") {
00173           bits.erase(bits.begin() + i+2); // ..
00174           bits.erase(bits.begin() + i);   // foo
00175           again = true;
00176         }
00177 
00178         // remove /. altogether
00179         if (i+1<bits.size() && bits[i]=="/" && bits[i+1]==".") {
00180           bits.erase(bits.begin() + i+1); // /
00181           bits.erase(bits.begin() + i);   // .
00182           again = true;
00183         }
00184       }
00185       if (!again)
00186         break;
00187     }
00188 
00189     // recompose the path from its bits
00190     path = "";
00191     for (unsigned int i=0; i<bits.size(); ++i)
00192       path += bits[i];
00193 #ifdef DEBUG
00194     vcl_cerr << "recomposed : " << path << '\n';
00195 #endif
00196   }
00197 
00198   // look for symbolic links to expand
00199   for (unsigned int i=1; i<=path.size(); ++i)
00200   {
00201     if (i==path.size() || path[i] == '/')
00202     {
00203       vcl_string sub(path.c_str(), path.c_str() + i);
00204       char buf[4096];
00205       int len = readlink(sub.c_str(), buf, sizeof buf);
00206       if (len != -1)
00207       {
00208         // it's a symlink. we should expand it and recurse.
00209 #ifdef DEBUG
00210         vcl_cerr << "before expansion : " << path << '\n';
00211 #endif
00212         if (buf[0] == '/') {
00213           // the target of the link starts with '/' so must be an
00214           // absolute path : ...foo/bar/etc... => buf/etc...
00215           path = vcl_string(buf, buf+len) + vcl_string(path.c_str() + i);
00216         }
00217         else
00218         {
00219           // the target is relative to the symlink's directory.
00220           int j=i-1;
00221           while (j>=0 && path[j] != '/')
00222             --j;
00223           if (j>=0) {
00224             // found another slash :   ...foo/bar/etc... where bar is the symlink.
00225             vcl_string a = vcl_string(path.c_str(), path.c_str()+j+1);
00226             vcl_string b = vcl_string(buf, buf+len);
00227             vcl_string c = vcl_string(path.c_str() + i, path.c_str() + path.size());
00228 #ifdef DEBUG
00229             vcl_cerr << "a = " << a << "\nb = " << b << "\nc = " << c << '\n';
00230 #endif
00231             path = a + b + c;
00232           }
00233           else {
00234             // gurgle. only one slash. must be : /bar/etc where bar is the symlink.
00235             path = vcl_string(buf, buf+len) + vcl_string(path.c_str() + i);
00236           }
00237         }
00238 
00239 #ifdef DEBUG
00240         vcl_cerr << "after expansion : " << path << '\n';
00241 #endif
00242         return vul_expand_path_internal(path);
00243       }
00244     }
00245   }
00246 
00247   // no more ideas
00248   return path;
00249 }
00250 
00251 typedef vcl_map<vcl_string, vcl_string, vcl_less<vcl_string> > map_t;
00252 
00253 vcl_string vul_expand_path(vcl_string path)
00254 {
00255   // create the cache.
00256   static map_t the_map;
00257 
00258   // look for the given path in the map.
00259   map_t::iterator i = the_map.find(path);
00260 
00261   if (i == the_map.end()) {
00262     // not in the map, so compute it :
00263     vcl_string mapped = vul_expand_path_internal(path);
00264     // cache it :
00265     i = the_map.insert(map_t::value_type(path, mapped)).first;
00266   }
00267 
00268   //
00269   return (*i).second;
00270 }
00271 
00272 vcl_string vul_expand_path_uncached(vcl_string path)
00273 {
00274   return vul_expand_path_internal(path);
00275 }
00276 
00277 #endif // VCL_WIN32

Generated on Thu Jan 10 14:41:00 2008 for core/vul by  doxygen 1.4.4