~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

[merge] bzr.dev 2255

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Bazaar -- distributed version control
2
 
#
3
 
# Copyright (C) 2005 by Canonical Ltd
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
4
2
#
5
3
# This program is free software; you can redistribute it and/or modify
6
4
# it under the terms of the GNU General Public License as published by
17
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
16
 
19
17
from cStringIO import StringIO
 
18
import os
 
19
import re
 
20
import stat
 
21
from stat import (S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE,
 
22
                  S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
 
23
import sys
 
24
import time
 
25
 
 
26
from bzrlib.lazy_import import lazy_import
 
27
lazy_import(globals(), """
 
28
import codecs
 
29
from datetime import datetime
20
30
import errno
21
31
from ntpath import (abspath as _nt_abspath,
22
32
                    join as _nt_join,
24
34
                    realpath as _nt_realpath,
25
35
                    splitdrive as _nt_splitdrive,
26
36
                    )
27
 
import os
28
 
from os import listdir
29
37
import posixpath
30
 
import re
31
38
import sha
32
39
import shutil
33
 
from shutil import copyfile
34
 
import stat
35
 
from stat import (S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE,
36
 
                  S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
37
 
import string
38
 
import sys
39
 
import time
40
 
import types
 
40
from shutil import (
 
41
    rmtree,
 
42
    )
41
43
import tempfile
 
44
from tempfile import (
 
45
    mkdtemp,
 
46
    )
42
47
import unicodedata
43
48
 
 
49
from bzrlib import (
 
50
    errors,
 
51
    )
 
52
""")
 
53
 
44
54
import bzrlib
45
 
from bzrlib.errors import (BzrError,
46
 
                           BzrBadParameterNotUnicode,
47
 
                           NoSuchFile,
48
 
                           PathNotChild,
49
 
                           IllegalPath,
50
 
                           )
51
 
from bzrlib.symbol_versioning import (deprecated_function, 
52
 
        zero_nine)
 
55
from bzrlib.symbol_versioning import (
 
56
    deprecated_function,
 
57
    zero_nine,
 
58
    )
53
59
from bzrlib.trace import mutter
54
60
 
55
61
 
122
128
        return _mapper(_lstat(f).st_mode)
123
129
    except OSError, e:
124
130
        if getattr(e, 'errno', None) == errno.ENOENT:
125
 
            raise bzrlib.errors.NoSuchFile(f)
 
131
            raise errors.NoSuchFile(f)
126
132
        raise
127
133
 
128
134
 
144
150
    elif kind == 'symlink':
145
151
        return '@'
146
152
    else:
147
 
        raise BzrError('invalid file kind %r' % kind)
 
153
        raise errors.BzrError('invalid file kind %r' % kind)
148
154
 
149
155
lexists = getattr(os.path, 'lexists', None)
150
156
if lexists is None:
159
165
            if e.errno == errno.ENOENT:
160
166
                return False;
161
167
            else:
162
 
                raise BzrError("lstat/stat of (%r): %r" % (f, e))
 
168
                raise errors.BzrError("lstat/stat of (%r): %r" % (f, e))
163
169
 
164
170
 
165
171
def fancy_rename(old, new, rename_func, unlink_func):
186
192
    file_existed = False
187
193
    try:
188
194
        rename_func(new, tmp_name)
189
 
    except (NoSuchFile,), e:
 
195
    except (errors.NoSuchFile,), e:
190
196
        pass
191
197
    except IOError, e:
192
198
        # RBC 20060103 abstraction leakage: the paramiko SFTP clients rename
221
227
# choke on a Unicode string containing a relative path if
222
228
# os.getcwd() returns a non-sys.getdefaultencoding()-encoded
223
229
# string.
224
 
_fs_enc = sys.getfilesystemencoding()
 
230
_fs_enc = sys.getfilesystemencoding() or 'utf-8'
225
231
def _posix_abspath(path):
226
232
    # jam 20060426 rather than encoding to fsencoding
227
233
    # copy posixpath.abspath, but use os.getcwdu instead
302
308
pathjoin = os.path.join
303
309
normpath = os.path.normpath
304
310
getcwd = os.getcwdu
305
 
mkdtemp = tempfile.mkdtemp
306
311
rename = os.rename
307
312
dirname = os.path.dirname
308
313
basename = os.path.basename
309
 
rmtree = shutil.rmtree
 
314
split = os.path.split
 
315
splitext = os.path.splitext
 
316
# These were already imported into local scope
 
317
# mkdtemp = tempfile.mkdtemp
 
318
# rmtree = shutil.rmtree
310
319
 
311
320
MIN_ABS_PATHLENGTH = 1
312
321
 
326
335
        """Error handler for shutil.rmtree function [for win32]
327
336
        Helps to remove files and dirs marked as read-only.
328
337
        """
329
 
        type_, value = excinfo[:2]
 
338
        exception = excinfo[1]
330
339
        if function in (os.remove, os.rmdir) \
331
 
            and type_ == OSError \
332
 
            and value.errno == errno.EACCES:
333
 
            bzrlib.osutils.make_writable(path)
 
340
            and isinstance(exception, OSError) \
 
341
            and exception.errno == errno.EACCES:
 
342
            make_writable(path)
334
343
            function(path)
335
344
        else:
336
345
            raise
366
375
            mutter('encoding stdout as sys.stdin encoding %r', output_encoding)
367
376
    else:
368
377
        mutter('encoding stdout as sys.stdout encoding %r', output_encoding)
 
378
    if output_encoding == 'cp0':
 
379
        # invalid encoding (cp0 means 'no codepage' on Windows)
 
380
        output_encoding = bzrlib.user_encoding
 
381
        mutter('cp0 is invalid encoding.'
 
382
               ' encoding stdout as bzrlib.user_encoding %r', output_encoding)
 
383
    # check encoding
 
384
    try:
 
385
        codecs.lookup(output_encoding)
 
386
    except LookupError:
 
387
        sys.stderr.write('bzr: warning:'
 
388
                         ' unknown terminal encoding %s.\n'
 
389
                         '  Using encoding %s instead.\n'
 
390
                         % (output_encoding, bzrlib.user_encoding)
 
391
                        )
 
392
        output_encoding = bzrlib.user_encoding
 
393
 
369
394
    return output_encoding
370
395
 
371
396
 
440
465
    
441
466
    The empty string as a dir name is taken as top-of-tree and matches 
442
467
    everything.
443
 
    
444
 
    >>> is_inside('src', pathjoin('src', 'foo.c'))
445
 
    True
446
 
    >>> is_inside('src', 'srccontrol')
447
 
    False
448
 
    >>> is_inside('src', pathjoin('src', 'a', 'a', 'a', 'foo.c'))
449
 
    True
450
 
    >>> is_inside('foo.c', 'foo.c')
451
 
    True
452
 
    >>> is_inside('foo.c', '')
453
 
    False
454
 
    >>> is_inside('', 'foo.c')
455
 
    True
456
468
    """
457
469
    # XXX: Most callers of this can actually do something smarter by 
458
470
    # looking at the inventory
554
566
 
555
567
def local_time_offset(t=None):
556
568
    """Return offset of local zone from GMT, either at present or at time t."""
557
 
    # python2.3 localtime() can't take None
558
569
    if t is None:
559
570
        t = time.time()
560
 
        
561
 
    if time.localtime(t).tm_isdst and time.daylight:
562
 
        return -time.altzone
563
 
    else:
564
 
        return -time.timezone
 
571
    offset = datetime.fromtimestamp(t) - datetime.utcfromtimestamp(t)
 
572
    return offset.days * 86400 + offset.seconds
565
573
 
566
574
    
567
575
def format_date(t, offset=0, timezone='original', date_fmt=None, 
581
589
        tt = time.localtime(t)
582
590
        offset = local_time_offset(t)
583
591
    else:
584
 
        raise BzrError("unsupported timezone format %r" % timezone,
585
 
                       ['options are "utc", "original", "local"'])
 
592
        raise errors.BzrError("unsupported timezone format %r" % timezone,
 
593
                              ['options are "utc", "original", "local"'])
586
594
    if date_fmt is None:
587
595
        date_fmt = "%a %Y-%m-%d %H:%M:%S"
588
596
    if show_offset:
659
667
except (NotImplementedError, AttributeError):
660
668
    # If python doesn't have os.urandom, or it doesn't work,
661
669
    # then try to first pull random data from /dev/urandom
662
 
    if os.path.exists("/dev/urandom"):
 
670
    try:
663
671
        rand_bytes = file('/dev/urandom', 'rb').read
664
672
    # Otherwise, use this hack as a last resort
665
 
    else:
 
673
    except (IOError, OSError):
666
674
        # not well seeded, but better than nothing
667
675
        def rand_bytes(n):
668
676
            import random
690
698
## decomposition (might be too tricksy though.)
691
699
 
692
700
def splitpath(p):
693
 
    """Turn string into list of parts.
694
 
 
695
 
    >>> splitpath('a')
696
 
    ['a']
697
 
    >>> splitpath('a/b')
698
 
    ['a', 'b']
699
 
    >>> splitpath('a/./b')
700
 
    ['a', 'b']
701
 
    >>> splitpath('a/.b')
702
 
    ['a', '.b']
703
 
    >>> splitpath('a/../b')
704
 
    Traceback (most recent call last):
705
 
    ...
706
 
    BzrError: sorry, '..' not allowed in path
707
 
    """
708
 
    assert isinstance(p, types.StringTypes)
 
701
    """Turn string into list of parts."""
 
702
    assert isinstance(p, basestring)
709
703
 
710
704
    # split on either delimiter because people might use either on
711
705
    # Windows
714
708
    rps = []
715
709
    for f in ps:
716
710
        if f == '..':
717
 
            raise BzrError("sorry, %r not allowed in path" % f)
 
711
            raise errors.BzrError("sorry, %r not allowed in path" % f)
718
712
        elif (f == '.') or (f == ''):
719
713
            pass
720
714
        else:
725
719
    assert isinstance(p, list)
726
720
    for f in p:
727
721
        if (f == '..') or (f is None) or (f == ''):
728
 
            raise BzrError("sorry, %r not allowed in path" % f)
 
722
            raise errors.BzrError("sorry, %r not allowed in path" % f)
729
723
    return pathjoin(*p)
730
724
 
731
725
 
753
747
def link_or_copy(src, dest):
754
748
    """Hardlink a file, or copy it if it can't be hardlinked."""
755
749
    if not hardlinks_good():
756
 
        copyfile(src, dest)
 
750
        shutil.copyfile(src, dest)
757
751
        return
758
752
    try:
759
753
        os.link(src, dest)
760
754
    except (OSError, IOError), e:
761
755
        if e.errno != errno.EXDEV:
762
756
            raise
763
 
        copyfile(src, dest)
 
757
        shutil.copyfile(src, dest)
764
758
 
765
759
def delete_any(full_path):
766
760
    """Delete a file or directory."""
782
776
 
783
777
def contains_whitespace(s):
784
778
    """True if there are any whitespace characters in s."""
785
 
    for ch in string.whitespace:
 
779
    # string.whitespace can include '\xa0' in certain locales, because it is
 
780
    # considered "non-breaking-space" as part of ISO-8859-1. But it
 
781
    # 1) Isn't a breaking whitespace
 
782
    # 2) Isn't one of ' \t\r\n' which are characters we sometimes use as
 
783
    #    separators
 
784
    # 3) '\xa0' isn't unicode safe since it is >128.
 
785
    # So we are following textwrap's example and hard-coding our own.
 
786
    # We probably could ignore \v and \f, too.
 
787
    for ch in u' \t\n\r\v\f':
786
788
        if ch in s:
787
789
            return True
788
790
    else:
824
826
        if tail:
825
827
            s.insert(0, tail)
826
828
    else:
827
 
        raise PathNotChild(rp, base)
 
829
        raise errors.PathNotChild(rp, base)
828
830
 
829
831
    if s:
830
832
        return pathjoin(*s)
845
847
    try:
846
848
        return unicode_or_utf8_string.decode('utf8')
847
849
    except UnicodeDecodeError:
848
 
        raise BzrBadParameterNotUnicode(unicode_or_utf8_string)
 
850
        raise errors.BzrBadParameterNotUnicode(unicode_or_utf8_string)
849
851
 
850
852
 
851
853
_platform_normalizes_filenames = False
920
922
    return sys.platform != "win32"
921
923
 
922
924
 
 
925
def supports_posix_readonly():
 
926
    """Return True if 'readonly' has POSIX semantics, False otherwise.
 
927
 
 
928
    Notably, a win32 readonly file cannot be deleted, unlike POSIX where the
 
929
    directory controls creation/deletion, etc.
 
930
 
 
931
    And under win32, readonly means that the directory itself cannot be
 
932
    deleted.  The contents of a readonly directory can be changed, unlike POSIX
 
933
    where files in readonly directories cannot be added, deleted or renamed.
 
934
    """
 
935
    return sys.platform != "win32"
 
936
 
 
937
 
923
938
def set_or_unset_env(env_variable, value):
924
939
    """Modify the environment, setting or removing the env_variable.
925
940
 
950
965
    if sys.platform != "win32":
951
966
        return
952
967
    if _validWin32PathRE.match(path) is None:
953
 
        raise IllegalPath(path)
 
968
        raise errors.IllegalPath(path)
954
969
 
955
970
 
956
971
def walkdirs(top, prefix=""):
989
1004
    lstat = os.lstat
990
1005
    pending = []
991
1006
    _directory = _directory_kind
992
 
    _listdir = listdir
 
1007
    _listdir = os.listdir
993
1008
    pending = [(prefix, "", _directory, None, top)]
994
1009
    while pending:
995
1010
        dirblock = []
1073
1088
_cached_user_encoding = None
1074
1089
 
1075
1090
 
1076
 
def get_user_encoding():
 
1091
def get_user_encoding(use_cache=True):
1077
1092
    """Find out what the preferred user encoding is.
1078
1093
 
1079
1094
    This is generally the encoding that is used for command line parameters
1080
1095
    and file contents. This may be different from the terminal encoding
1081
1096
    or the filesystem encoding.
1082
1097
 
 
1098
    :param  use_cache:  Enable cache for detected encoding.
 
1099
                        (This parameter is turned on by default,
 
1100
                        and required only for selftesting)
 
1101
 
1083
1102
    :return: A string defining the preferred user encoding
1084
1103
    """
1085
1104
    global _cached_user_encoding
1086
 
    if _cached_user_encoding is not None:
 
1105
    if _cached_user_encoding is not None and use_cache:
1087
1106
        return _cached_user_encoding
1088
1107
 
1089
1108
    if sys.platform == 'darwin':
1097
1116
        import locale
1098
1117
 
1099
1118
    try:
1100
 
        _cached_user_encoding = locale.getpreferredencoding()
 
1119
        user_encoding = locale.getpreferredencoding()
1101
1120
    except locale.Error, e:
1102
1121
        sys.stderr.write('bzr: warning: %s\n'
1103
1122
                         '  Could not determine what text encoding to use.\n'
1105
1124
                         '  doesn\'t support the locale set by $LANG (%s)\n'
1106
1125
                         "  Continuing with ascii encoding.\n"
1107
1126
                         % (e, os.environ.get('LANG')))
1108
 
 
1109
 
    if _cached_user_encoding is None:
1110
 
        _cached_user_encoding = 'ascii'
1111
 
    return _cached_user_encoding
 
1127
        user_encoding = 'ascii'
 
1128
 
 
1129
    # Windows returns 'cp0' to indicate there is no code page. So we'll just
 
1130
    # treat that as ASCII, and not support printing unicode characters to the
 
1131
    # console.
 
1132
    if user_encoding in (None, 'cp0'):
 
1133
        user_encoding = 'ascii'
 
1134
    else:
 
1135
        # check encoding
 
1136
        try:
 
1137
            codecs.lookup(user_encoding)
 
1138
        except LookupError:
 
1139
            sys.stderr.write('bzr: warning:'
 
1140
                             ' unknown encoding %s.'
 
1141
                             ' Continuing with ascii encoding.\n'
 
1142
                             % user_encoding
 
1143
                            )
 
1144
            user_encoding = 'ascii'
 
1145
 
 
1146
    if use_cache:
 
1147
        _cached_user_encoding = user_encoding
 
1148
 
 
1149
    return user_encoding
 
1150
 
 
1151
 
 
1152
def recv_all(socket, bytes):
 
1153
    """Receive an exact number of bytes.
 
1154
 
 
1155
    Regular Socket.recv() may return less than the requested number of bytes,
 
1156
    dependning on what's in the OS buffer.  MSG_WAITALL is not available
 
1157
    on all platforms, but this should work everywhere.  This will return
 
1158
    less than the requested amount if the remote end closes.
 
1159
 
 
1160
    This isn't optimized and is intended mostly for use in testing.
 
1161
    """
 
1162
    b = ''
 
1163
    while len(b) < bytes:
 
1164
        new = socket.recv(bytes - len(b))
 
1165
        if new == '':
 
1166
            break # eof
 
1167
        b += new
 
1168
    return b
 
1169
 
 
1170
def dereference_path(path):
 
1171
    """Determine the real path to a file.
 
1172
 
 
1173
    All parent elements are dereferenced.  But the file itself is not
 
1174
    dereferenced.
 
1175
    :param path: The original path.  May be absolute or relative.
 
1176
    :return: the real path *to* the file
 
1177
    """
 
1178
    parent, base = os.path.split(path)
 
1179
    # The pathjoin for '.' is a workaround for Python bug #1213894.
 
1180
    # (initial path components aren't dereferenced)
 
1181
    return pathjoin(realpath(pathjoin('.', parent)), base)