[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: svnadmin dump/verify/load uses svn_path_join

From: Jonathan Gilbert <o2w9gs702_at_sneakemail.com>
Date: 2007-03-24 16:10:54 CET

At 02:47 PM 3/24/2007 +0100, Lieven Govaerts wrote:
>>>> How is "c:hi" an absolute path on Windows? Isn't "c:hi" really the
>>>> equivalent of "c:.\hi"?
>>>
>>> Depending on the way you look at 'c:hi' it's both absolute
>>> and relative.
>>> Your example makes it look like a relative path. However, a
>>> relative path can be added to the current working directory
>>> to make an absolute path, but you can't do that with 'c:hi'.
[snip]
>Now take this example:
>c:\svn\wc\> svn diff G:test.txt
>
>If 'G:test.txt' would be considered a relative path, then Subversion
>would think 'c:\svn\wc\G:test.txt' is the absolute path for test.txt,
>which clearly is incorrect. So for practical reasons we consider
>'G:test.txt' as an absolute path, but you're right in saying
>conceptually it's actually relative.

The problem here is evidently that Windows has a "feature" that other
operating systems don't have: It has multiple filesystem roots, and each
root gets its own current directory for a given application. The
application also has a "current drive", conceptually, which identifies
which of the CWDs across all the drives is actually the current one. The C
standard doesn't acknowledge this, obviously; getcwd() can only return a
single value. Even within the Win32 API, GetCurrentDirectory() behaves like
getcwd(), but there is an alternate function GetFullPathName() which can be
used to find the current directory on other drives (one passes it a drive
letter and a colon; the current directory on that drive is then assumed).
The fact is, though, that Subversion (or the platform layer it is upon) do
not consider the possibility of multiple filesystem roots, and thus the API
functions involved don't have any way of taking these into consideration.

On Windows, the function to concatenate paths must be smart enough to
recognize drive letters at the start of a path and not use the left-hand
"parent directory" argument at all. Having detected that, if there is no
path separator character (Windows permitting both '/' and '\\'), if an
absolute path is desired by the caller, the path then needs to be expanded
against the drive letter that was provided (the aforementioned
GetFullPathName() API does this as well).

To complicate things, Windows considers a UNC path specifying a share name
to be a valid "drive letter" (though applications do not remember current
directories relative to these beyond where the current directory *is* on a
UNC path). That is, you can call
SetCurrentDirectory("\\\\SERVER\\Share\\Path\\To\\Directory\\"), and
"\\\\SERVER\\SHARE" is considered to be the "drive-letter" part. A
subsequent call to SetCurrentDirectory("\\Another\\Directory\\") (note the
lack of a drive letter) will be processed as relative to
"\\\\SERVER\\SHARE". I don't know if this requires any special handling
within Subversion, or if it "just works" because the values that filter
down to the API happen to do the right thing. I'm fairly certain Windows is
also the only OS with this complication as well, though :-)

Pseudocode for a Win32 combine_path function which takes drive letters into
account and always returns an absolute path:

bool starts_with_drive_letter(string path)
{
  return (path[0].IsLetter && (path[1] == ':'));
}

bool starts_with_unc_share(string path)
{
  // could definitely be improved:
  return path.StartsWith("\\\\");
}

string extract_unc_share(string path)
{
  int third_backslash = path.IndexOf('\\', 2); // skip the first two '\\'s

  if (third_backslash < 0)
    return path;

  int fourth_backslash = path.IndexOf('\\', third_backslash + 1);

  if (fourth_backslash < 0)
    return path;

  return path.Substring(0, fourth_backslash);
}

string combine_path(string base, string subpath)
{
  if (starts_with_drive_letter(subpath)
   || starts_with_unc_share(subpath))
    return GetFullPathName(subpath);
  else
  {
    if (subpath.StartsWith('/' or '\\'))
    {
      if (starts_with_unc_share(base))
        return combine_path(
          extract_unc_share(base),
          subpath.Substring(1));
      else if (starts_with_drive_letter(base))
        return base.Substring(0, 2) + subpath;
      else
        return combine_path(
          GetFullPathName(base),
          subpath);
    }
    else
    {
      if (base.EndsWith('/' or '\\'))
        return base + subpath;
      else
        return base + '\\' + subpath;
    }
  }
}

I don't think I've missed any cases, but the code is obviously untested ;-)

Hopefully someone can find this helpful.

Jonathan Gilbert

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Sat Mar 24 17:13:24 2007

This is an archived mail posted to the Subversion Dev mailing list.