~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/urlutils.py

  • Committer: Robert Collins
  • Date: 2006-08-08 23:19:29 UTC
  • mfrom: (1884 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1912.
  • Revision ID: robertc@robertcollins.net-20060808231929-4e3e298190214b3a
current status

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
"""A collection of function for handling URL operations."""
20
20
 
21
21
import os
22
 
from posixpath import split as _posix_split
 
22
from posixpath import split as _posix_split, normpath as _posix_normpath
23
23
import re
24
24
import sys
25
25
import urllib
116
116
    if m:
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
 
123
            path = path[:-1]
119
124
    else:
120
125
        path = base.split('/')
121
126
 
160
165
    """
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)))
165
170
 
166
171
 
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:])
180
185
 
181
186
 
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
184
189
 
185
190
    This also handles transforming escaping unicode characters, etc.
186
191
    """
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:/')
197
207
 
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
201
211
 
202
 
    MIN_ABS_FILEURL_LENGTH = len('file:///C|/')
 
212
    MIN_ABS_FILEURL_LENGTH = WIN32_MIN_ABS_FILEURL_LENGTH
203
213
 
204
214
 
205
215
_url_scheme_re = re.compile(r'^(?P<scheme>[^:/]{2,})://(?P<path>.*)$')
291
301
    return "/".join(output_sections) or "."
292
302
 
293
303
 
 
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
 
314
 
 
315
 
294
316
def split(url, exclude_trailing_slash=True):
295
317
    """Split a URL into its parent directory and a child directory.
296
318
 
320
342
 
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|/
327
 
        path = path[3:]
 
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
328
349
 
329
350
    if exclude_trailing_slash and len(path) > 1 and path.endswith('/'):
330
351
        path = path[:-1]
332
353
    return url_base + head, tail
333
354
 
334
355
 
 
356
def _win32_strip_local_trailing_slash(url):
 
357
    """Strip slashes after the drive letter"""
 
358
    if len(url) > WIN32_MIN_ABS_FILEURL_LENGTH:
 
359
        return url[:-1]
 
360
    else:
 
361
        return url
 
362
 
 
363
 
335
364
def strip_trailing_slash(url):
336
365
    """Strip trailing slash, except for root paths.
337
366
 
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:/
355
384
    """
356
385
    if not url.endswith('/'):
357
386
        # Nothing to do
358
387
        return url
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:
363
 
            return url[:-1]
364
 
        else:
365
 
            return url
 
389
        return _win32_strip_local_trailing_slash(url)
366
390
 
367
391
    scheme_loc, first_path_slash = _find_scheme_and_separator(url)
368
392
    if scheme_loc is None:
435
459
    :return: A unicode string which can be safely encoded into the 
436
460
         specified encoding.
437
461
    """
 
462
    assert encoding is not None, 'you cannot specify None for the display encoding.'
438
463
    if url.startswith('file://'):
439
464
        try:
440
465
            path = local_path_from_url(url)