Bruce Momjian <pgman@candle.pha.pa.us> writes:
> In my first attempt, I counted the number of ".." groups, then went up
> to remove each "..", and them remove a regular directory for each "..".
> And then you have this case:
> /usr/local/../bin/../..
> Here you hit the first ".." as you are going up. It just seemed like a
> lost cause.
BTW, you were right: this is a *lot* harder than it looks at first
glance. Here's what I ended up with:
/*
* Remove any trailing uses of "." and process ".." ourselves
*
* Note that "/../.." should reduce to just "/", while "../.." has to
* be kept as-is. In the latter case we put back mistakenly trimmed
* ".." components below. Also note that we want a Windows drive spec
* to be visible to trim_directory(), but it's not part of the logic
* that's looking at the name components; hence distinction between
* path and spath.
*/
spath = skip_drive(path);
pending_strips = 0;
for (;;)
{
int len = strlen(spath);
if (len >= 2 && strcmp(spath + len - 2, "/.") == 0)
trim_directory(path);
else if (strcmp(spath, ".") == 0)
{
/* Want to leave "." alone, but "./.." has to become ".." */
if (pending_strips > 0)
*spath = '\0';
break;
}
else if ((len >= 3 && strcmp(spath + len - 3, "/..") == 0) ||
strcmp(spath, "..") == 0)
{
trim_directory(path);
pending_strips++;
}
else if (pending_strips > 0 && *spath != '\0')
{
/* trim a regular directory name cancelled by ".." */
trim_directory(path);
pending_strips--;
/* foo/.. should become ".", not empty */
if (*spath == '\0')
strcpy(spath, ".");
}
else
break;
}
if (pending_strips > 0)
{
/*
* We could only get here if path is now totally empty (other than
* a possible drive specifier on Windows).
* We have to put back one or more ".."'s that we took off.
*/
while (--pending_strips > 0)
strcat(path, "../");
strcat(path, "..");
}
}
regards, tom lane