117
117
scheme = m.group('scheme')
118
118
path = m.group('path').split('/')
119
if path[-1:] == ['']:
120
# Strip off a trailing slash
121
# This helps both when we are at the root, and when
122
# 'base' has an extra slash at the end
120
125
path = base.split('/')
161
166
# importing directly from posixpath allows us to test this
162
167
# on non-posix platforms
163
from posixpath import normpath
164
return 'file://' + escape(normpath(bzrlib.osutils._posix_abspath(path)))
168
return 'file://' + escape(_posix_normpath(
169
bzrlib.osutils._posix_abspath(path)))
167
172
def _win32_local_path_from_url(url):
168
"""Convert a url like file:///C|/path/to/foo into C:/path/to/foo"""
173
"""Convert a url like file:///C:/path/to/foo into C:/path/to/foo"""
169
174
if not url.startswith('file:///'):
170
175
raise errors.InvalidURL(url, 'local urls must start with file:///')
171
176
# We strip off all 3 slashes
172
177
win32_url = url[len('file:///'):]
173
if (win32_url[0] not in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
178
if (win32_url[0] not in ('abcdefghijklmnopqrstuvwxyz'
179
'ABCDEFGHIJKLMNOPQRSTUVWXYZ')
174
180
or win32_url[1] not in '|:'
175
181
or win32_url[2] != '/'):
176
raise errors.InvalidURL(url, 'Win32 file urls start with file:///X|/, where X is a valid drive letter')
177
# TODO: jam 20060426, we could .upper() or .lower() the drive letter
178
# for better consistency.
182
raise errors.InvalidURL(url, 'Win32 file urls start with'
183
' file:///x:/, where x is a valid drive letter')
179
184
return win32_url[0].upper() + u':' + unescape(win32_url[2:])
182
187
def _win32_local_path_to_url(path):
183
"""Convert a local path like ./foo into a URL like file:///C|/path/to/foo
188
"""Convert a local path like ./foo into a URL like file:///C:/path/to/foo
185
190
This also handles transforming escaping unicode characters, etc.
187
192
# importing directly from ntpath allows us to test this
188
# on non-win32 platforms
193
# on non-win32 platform
194
# FIXME: It turns out that on nt, ntpath.abspath uses nt._getfullpathname
195
# which actually strips trailing space characters.
196
# The worst part is that under linux ntpath.abspath has different
197
# semantics, since 'nt' is not an available module.
189
198
win32_path = bzrlib.osutils._nt_normpath(
190
199
bzrlib.osutils._win32_abspath(path)).replace('\\', '/')
191
200
return 'file:///' + win32_path[0].upper() + ':' + escape(win32_path[2:])
194
203
local_path_to_url = _posix_local_path_to_url
195
204
local_path_from_url = _posix_local_path_from_url
196
205
MIN_ABS_FILEURL_LENGTH = len('file:///')
206
WIN32_MIN_ABS_FILEURL_LENGTH = len('file:///C:/')
198
208
if sys.platform == 'win32':
199
209
local_path_to_url = _win32_local_path_to_url
200
210
local_path_from_url = _win32_local_path_from_url
202
MIN_ABS_FILEURL_LENGTH = len('file:///C|/')
212
MIN_ABS_FILEURL_LENGTH = WIN32_MIN_ABS_FILEURL_LENGTH
205
215
_url_scheme_re = re.compile(r'^(?P<scheme>[^:/]{2,})://(?P<path>.*)$')
291
301
return "/".join(output_sections) or "."
304
def _win32_extract_drive_letter(url_base, path):
305
"""On win32 the drive letter needs to be added to the url base."""
306
# Strip off the drive letter
307
# path is currently /C:/foo
308
if len(path) < 3 or path[2] not in ':|' or path[3] != '/':
309
raise errors.InvalidURL(url_base + path,
310
'win32 file:/// paths need a drive letter')
311
url_base += path[0:3] # file:// + /C:
312
path = path[3:] # /foo
313
return url_base, path
294
316
def split(url, exclude_trailing_slash=True):
295
317
"""Split a URL into its parent directory and a child directory.
321
343
if sys.platform == 'win32' and url.startswith('file:///'):
322
344
# Strip off the drive letter
323
if path[2:3] not in '\\/':
324
raise errors.InvalidURL(url,
325
'win32 file:/// paths need a drive letter')
326
url_base += path[1:4] # file:///C|/
345
# url_base is currently file://
346
# path is currently /C:/foo
347
url_base, path = _win32_extract_drive_letter(url_base, path)
348
# now it should be file:///C: and /foo
329
350
if exclude_trailing_slash and len(path) > 1 and path.endswith('/'):
332
353
return url_base + head, tail
356
def _win32_strip_local_trailing_slash(url):
357
"""Strip slashes after the drive letter"""
358
if len(url) > WIN32_MIN_ABS_FILEURL_LENGTH:
335
364
def strip_trailing_slash(url):
336
365
"""Strip trailing slash, except for root paths.
351
380
file:///foo/ => file:///foo
352
381
# This is unique on win32 platforms, and is the only URL
353
382
# format which does it differently.
354
file:///C|/ => file:///C|/
383
file:///c|/ => file:///c:/
356
385
if not url.endswith('/'):
359
388
if sys.platform == 'win32' and url.startswith('file:///'):
360
# This gets handled specially, because the 'top-level'
361
# of a win32 path is actually the drive letter
362
if len(url) > MIN_ABS_FILEURL_LENGTH:
389
return _win32_strip_local_trailing_slash(url)
367
391
scheme_loc, first_path_slash = _find_scheme_and_separator(url)
368
392
if scheme_loc is None: