15
15
# You should have received a copy of the GNU General Public License
16
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
19
"""A collection of function for handling URL operations."""
68
67
relpath = relpath.encode('utf-8')
69
68
# After quoting and encoding, the path should be perfectly
70
69
# safe as a plain ASCII string, str() just enforces this
71
return str(urllib.quote(relpath, safe='/~'))
70
return str(urllib.quote(relpath))
74
73
def file_relpath(base, path):
75
74
"""Compute just the relative sub-portion of a url
77
76
This assumes that both paths are already fully specified file:// URLs.
79
if len(base) < MIN_ABS_FILEURL_LENGTH:
80
raise ValueError('Length of base must be equal or'
81
' exceed the platform minimum url length (which is %d)' %
82
MIN_ABS_FILEURL_LENGTH)
78
assert len(base) >= MIN_ABS_FILEURL_LENGTH, ('Length of base must be equal or'
79
' exceed the platform minimum url length (which is %d)' %
80
MIN_ABS_FILEURL_LENGTH)
83
82
base = local_path_from_url(base)
84
83
path = local_path_from_url(path)
85
84
return escape(osutils.relpath(base, path))
186
185
def joinpath(base, *args):
187
186
"""Join URL path segments to a URL path segment.
189
188
This is somewhat like osutils.joinpath, but intended for URLs.
191
190
XXX: this duplicates some normalisation logic, and also duplicates a lot of
231
230
This also handles transforming escaping unicode characters, etc.
233
# importing directly from posixpath allows us to test this
232
# importing directly from posixpath allows us to test this
234
233
# on non-posix platforms
235
234
return 'file://' + escape(_posix_normpath(
236
235
osutils._posix_abspath(path)))
250
249
raise errors.InvalidURL(url, 'Win32 UNC path urls'
251
250
' have form file://HOST/path')
252
251
return unescape(win32_url)
254
# allow empty paths so we can serve all roots
255
if win32_url == '///':
258
252
# usual local path with drive letter
259
253
if (win32_url[3] not in ('abcdefghijklmnopqrstuvwxyz'
260
254
'ABCDEFGHIJKLMNOPQRSTUVWXYZ')
271
265
This also handles transforming escaping unicode characters, etc.
273
# importing directly from ntpath allows us to test this
267
# importing directly from ntpath allows us to test this
274
268
# on non-win32 platform
275
269
# FIXME: It turns out that on nt, ntpath.abspath uses nt._getfullpathname
276
270
# which actually strips trailing space characters.
277
271
# The worst part is that under linux ntpath.abspath has different
278
272
# semantics, since 'nt' is not an available module.
282
273
win32_path = osutils._win32_abspath(path)
283
274
# check for UNC path \\HOST\path
284
275
if win32_path.startswith('//'):
370
361
dummy, base_first_slash = _find_scheme_and_separator(base)
371
362
if base_first_slash is None:
374
365
dummy, other_first_slash = _find_scheme_and_separator(other)
375
366
if other_first_slash is None:
418
409
# Strip off the drive letter
419
410
# path is currently /C:/foo
420
411
if len(path) < 3 or path[2] not in ':|' or path[3] != '/':
421
raise errors.InvalidURL(url_base + path,
412
raise errors.InvalidURL(url_base + path,
422
413
'win32 file:/// paths need a drive letter')
423
414
url_base += path[0:3] # file:// + /C:
424
415
path = path[3:] # /foo
432
423
:param exclude_trailing_slash: Strip off a final '/' if it is part
433
424
of the path (but not if it is part of the protocol specification)
435
:return: (parent_url, child_dir). child_dir may be the empty string if we're at
426
:return: (parent_url, child_dir). child_dir may be the empty string if we're at
438
429
scheme_loc, first_path_slash = _find_scheme_and_separator(url)
542
533
# These are characters that if escaped, should stay that way
543
534
_no_decode_chars = ';/?:@&=+$,#'
544
535
_no_decode_ords = [ord(c) for c in _no_decode_chars]
545
_no_decode_hex = (['%02x' % o for o in _no_decode_ords]
536
_no_decode_hex = (['%02x' % o for o in _no_decode_ords]
546
537
+ ['%02X' % o for o in _no_decode_ords])
547
538
_hex_display_map = dict(([('%02x' % o, chr(o)) for o in range(256)]
548
539
+ [('%02X' % o, chr(o)) for o in range(256)]))
574
565
This will turn file:// urls into local paths, and try to decode
575
566
any portions of a http:// style url that it can.
577
Any sections of the URL which can't be represented in the encoding or
568
Any sections of the URL which can't be represented in the encoding or
578
569
need to stay as escapes are left alone.
580
571
:param url: A 7-bit ASCII URL
581
572
:param encoding: The final output encoding
583
:return: A unicode string which can be safely encoded into the
574
:return: A unicode string which can be safely encoded into the
584
575
specified encoding.
587
raise ValueError('you cannot specify None for the display encoding')
577
assert encoding is not None, 'you cannot specify None for the display encoding.'
588
578
if url.startswith('file://'):
590
580
path = local_path_from_url(url)
645
635
return from_location[sep+1:]
647
637
return from_location
650
def _is_absolute(url):
651
return (osutils.pathjoin('/foo', url) == url)
654
def rebase_url(url, old_base, new_base):
655
"""Convert a relative path from an old base URL to a new base URL.
657
The result will be a relative path.
658
Absolute paths and full URLs are returned unaltered.
660
scheme, separator = _find_scheme_and_separator(url)
661
if scheme is not None:
663
if _is_absolute(url):
665
old_parsed = urlparse.urlparse(old_base)
666
new_parsed = urlparse.urlparse(new_base)
667
if (old_parsed[:2]) != (new_parsed[:2]):
668
raise errors.InvalidRebaseURLs(old_base, new_base)
669
return determine_relative_path(new_parsed[2],
670
join(old_parsed[2], url))
673
def determine_relative_path(from_path, to_path):
674
"""Determine a relative path from from_path to to_path."""
675
from_segments = osutils.splitpath(from_path)
676
to_segments = osutils.splitpath(to_path)
678
for count, (from_element, to_element) in enumerate(zip(from_segments,
680
if from_element != to_element:
684
unique_from = from_segments[count:]
685
unique_to = to_segments[count:]
686
segments = (['..'] * len(unique_from) + unique_to)
687
if len(segments) == 0:
689
return osutils.pathjoin(*segments)