ENH: Add SystemTools::SplitPathRootComponent and re-implement SplitPath to use it. Add better treatment of user home directory paths.

This commit is contained in:
Brad King 2008-01-11 08:33:48 -05:00
parent d7c7ab0927
commit a6d32b96ed
2 changed files with 123 additions and 44 deletions

View File

@ -2975,82 +2975,145 @@ kwsys_stl::string SystemTools::GetActualCaseForPath(const char* p)
}
//----------------------------------------------------------------------------
void SystemTools::SplitPath(const char* p,
kwsys_stl::vector<kwsys_stl::string>& components)
const char* SystemTools::SplitPathRootComponent(const char* p,
kwsys_stl::string* root)
{
components.clear();
// Identify the root component.
const char* c = p;
if((c[0] == '/' && c[1] == '/') || (c[0] == '\\' && c[1] == '\\'))
{
// Network path.
components.push_back("//");
if(root)
{
*root = "//";
}
c += 2;
}
else if(c[0] == '/')
{
// Unix path.
components.push_back("/");
if(root)
{
*root = "/";
}
c += 1;
}
else if(c[0] && c[1] == ':' && (c[2] == '/' || c[2] == '\\'))
{
// Windows path.
kwsys_stl::string root = "_:/";
root[0] = c[0];
components.push_back(root);
if(root)
{
(*root) = "_:/";
(*root)[0] = c[0];
}
c += 3;
}
else if(c[0] && c[1] == ':')
{
// Path relative to a windows drive working directory.
kwsys_stl::string root = "_:";
root[0] = c[0];
components.push_back(root);
if(root)
{
(*root) = "_:";
(*root)[0] = c[0];
}
c += 2;
}
#ifdef HAVE_GETPWNAM
else if(c[0] == '~')
{
int numChars = 1;
while(c[numChars] && c[numChars] != '/')
// Home directory. The returned root should always have a
// trailing slash so that appending components as
// c[0]c[1]/c[2]/... works. The remaining path returned should
// skip the first slash if it exists:
//
// "~" : root = "~/" , return ""
// "~/ : root = "~/" , return ""
// "~/x : root = "~/" , return "x"
// "~u" : root = "~u/", return ""
// "~u/" : root = "~u/", return ""
// "~u/x" : root = "~u/", return "x"
int n = 1;
while(c[n] && c[n] != '/')
{
numChars++;
++n;
}
const char* homedir;
if(numChars == 1)
if(root)
{
homedir = getenv("HOME");
root->assign(c, n);
*root += '/';
}
else
if(c[n] == '/')
{
char user[PATH_MAX];
strncpy(user, c+1, numChars-1);
user[numChars] = '\0';
passwd* pw = getpwnam(user);
if(p)
{
homedir = pw->pw_dir;
}
else
{
homedir = "";
}
++n;
}
kwsys_stl::vector<kwsys_stl::string> home_components;
SystemTools::SplitPath(homedir, home_components);
components.insert(components.end(),
home_components.begin(),
home_components.end());
c += numChars;
c += n;
}
#endif
else
{
// Relative path.
components.push_back("");
if(root)
{
*root = "";
}
}
// Return the remaining path.
return c;
}
//----------------------------------------------------------------------------
void SystemTools::SplitPath(const char* p,
kwsys_stl::vector<kwsys_stl::string>& components,
bool expand_home_dir)
{
const char* c = p;
components.clear();
// Identify the root component.
{
kwsys_stl::string root;
c = SystemTools::SplitPathRootComponent(c, &root);
// Expand home directory references if requested.
if(expand_home_dir && !root.empty() && root[0] == '~')
{
kwsys_stl::string homedir;
root = root.substr(0, root.size()-1);
if(root.size() == 1)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
if(const char* h = getenv("USERPROFILE"))
{
homedir = h;
}
else
#endif
if(const char* h = getenv("HOME"))
{
homedir = h;
}
}
#ifdef HAVE_GETPWNAM
else if(passwd* pw = getpwnam(root.c_str()+1))
{
if(pw->pw_dir)
{
homedir = pw->pw_dir;
}
}
#endif
if(!homedir.empty() && (homedir[homedir.size()-1] == '/' ||
homedir[homedir.size()-1] == '\\'))
{
homedir = homedir.substr(0, homedir.size()-1);
}
SystemTools::SplitPath(homedir.c_str(), components);
}
else
{
components.push_back(root);
}
}
// Parse the remaining components.
const char* first = c;
const char* last = first;

View File

@ -351,20 +351,36 @@ public:
const char* in_base);
/**
* Split a path name into its basic components. The first component
* is one of the following roots:
* "/" = UNIX
* Split a path name into its root component and the rest of the
* path. The root component is one of the following:
* "/" = UNIX full path
* "c:/" = Windows full path (can be any drive letter)
* "c:" = Windows drive-letter relative path (can be any drive letter)
* "//" = Network path
* "~" = Home path for current user
* "~u" = Home path for user 'u'
* "" = Relative path
*
* A pointer to the rest of the path after the root component is
* returned. The root component is stored in the "root" string if
* given.
*/
static const char* SplitPathRootComponent(const char* p,
kwsys_stl::string* root=0);
/**
* Split a path name into its basic components. The first component
* is one of the roots returned by SplitPathRootComponent.
* The remaining components form the path. If there is a trailing
* slash then the last component is the empty string. The
* components can be recombined as "c[0]c[1]/c[2]/.../c[n]" to
* produce the original path.
* produce the original path. Home directory references are
* automatically expanded if expand_home_dir is true and this
* platform supports them.
*/
static void SplitPath(const char* p,
kwsys_stl::vector<kwsys_stl::string>& components);
kwsys_stl::vector<kwsys_stl::string>& components,
bool expand_home_dir = true);
/**
* Join components of a path name into a single string. See