~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

Late bind to PatienceSequenceMatcher to allow plugin to override.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Bazaar -- distributed version control
 
1
# Bazaar-NG -- distributed version control
2
2
#
3
3
# Copyright (C) 2005 by Canonical Ltd
4
4
#
16
16
# along with this program; if not, write to the Free Software
17
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
18
 
 
19
from shutil import copyfile
 
20
from stat import (S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE,
 
21
                  S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
19
22
from cStringIO import StringIO
20
23
import errno
21
 
from ntpath import (abspath as _nt_abspath,
22
 
                    join as _nt_join,
23
 
                    normpath as _nt_normpath,
24
 
                    realpath as _nt_realpath,
25
 
                    splitdrive as _nt_splitdrive,
26
 
                    )
27
24
import os
28
 
from os import listdir
29
 
import posixpath
30
25
import re
31
26
import sha
32
27
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
28
import string
38
29
import sys
39
30
import time
40
31
import types
41
32
import tempfile
42
 
import unicodedata
43
33
 
44
34
import bzrlib
45
35
from bzrlib.errors import (BzrError,
48
38
                           PathNotChild,
49
39
                           IllegalPath,
50
40
                           )
51
 
from bzrlib.symbol_versioning import (deprecated_function, 
52
 
        zero_nine)
53
41
from bzrlib.trace import mutter
54
 
 
55
 
 
56
 
# On win32, O_BINARY is used to indicate the file should
57
 
# be opened in binary mode, rather than text mode.
58
 
# On other platforms, O_BINARY doesn't exist, because
59
 
# they always open in binary mode, so it is okay to
60
 
# OR with 0 on those platforms
61
 
O_BINARY = getattr(os, 'O_BINARY', 0)
 
42
import bzrlib.win32console
62
43
 
63
44
 
64
45
def make_readonly(filename):
93
74
        return f
94
75
 
95
76
 
96
 
_directory_kind = 'directory'
97
 
 
98
 
_formats = {
99
 
    stat.S_IFDIR:_directory_kind,
100
 
    stat.S_IFCHR:'chardev',
101
 
    stat.S_IFBLK:'block',
102
 
    stat.S_IFREG:'file',
103
 
    stat.S_IFIFO:'fifo',
104
 
    stat.S_IFLNK:'symlink',
105
 
    stat.S_IFSOCK:'socket',
106
 
}
107
 
 
108
 
 
109
 
def file_kind_from_stat_mode(stat_mode, _formats=_formats, _unknown='unknown'):
110
 
    """Generate a file kind from a stat mode. This is used in walkdirs.
111
 
 
112
 
    Its performance is critical: Do not mutate without careful benchmarking.
113
 
    """
114
 
    try:
115
 
        return _formats[stat_mode & 0170000]
116
 
    except KeyError:
117
 
        return _unknown
118
 
 
119
 
 
120
 
def file_kind(f, _lstat=os.lstat, _mapper=file_kind_from_stat_mode):
121
 
    try:
122
 
        return _mapper(_lstat(f).st_mode)
123
 
    except OSError, e:
124
 
        if getattr(e, 'errno', None) == errno.ENOENT:
125
 
            raise bzrlib.errors.NoSuchFile(f)
126
 
        raise
127
 
 
128
 
 
129
 
def get_umask():
130
 
    """Return the current umask"""
131
 
    # Assume that people aren't messing with the umask while running
132
 
    # XXX: This is not thread safe, but there is no way to get the
133
 
    #      umask without setting it
134
 
    umask = os.umask(0)
135
 
    os.umask(umask)
136
 
    return umask
 
77
def file_kind(f):
 
78
    mode = os.lstat(f)[ST_MODE]
 
79
    if S_ISREG(mode):
 
80
        return 'file'
 
81
    elif S_ISDIR(mode):
 
82
        return 'directory'
 
83
    elif S_ISLNK(mode):
 
84
        return 'symlink'
 
85
    elif S_ISCHR(mode):
 
86
        return 'chardev'
 
87
    elif S_ISBLK(mode):
 
88
        return 'block'
 
89
    elif S_ISFIFO(mode):
 
90
        return 'fifo'
 
91
    elif S_ISSOCK(mode):
 
92
        return 'socket'
 
93
    else:
 
94
        return 'unknown'
137
95
 
138
96
 
139
97
def kind_marker(kind):
140
98
    if kind == 'file':
141
99
        return ''
142
 
    elif kind == _directory_kind:
 
100
    elif kind == 'directory':
143
101
        return '/'
144
102
    elif kind == 'symlink':
145
103
        return '@'
146
104
    else:
147
105
        raise BzrError('invalid file kind %r' % kind)
148
106
 
149
 
lexists = getattr(os.path, 'lexists', None)
150
 
if lexists is None:
151
 
    def lexists(f):
152
 
        try:
153
 
            if hasattr(os, 'lstat'):
154
 
                os.lstat(f)
155
 
            else:
156
 
                os.stat(f)
157
 
            return True
158
 
        except OSError,e:
159
 
            if e.errno == errno.ENOENT:
160
 
                return False;
161
 
            else:
162
 
                raise BzrError("lstat/stat of (%r): %r" % (f, e))
163
 
 
 
107
def lexists(f):
 
108
    if hasattr(os.path, 'lexists'):
 
109
        return os.path.lexists(f)
 
110
    try:
 
111
        if hasattr(os, 'lstat'):
 
112
            os.lstat(f)
 
113
        else:
 
114
            os.stat(f)
 
115
        return True
 
116
    except OSError,e:
 
117
        if e.errno == errno.ENOENT:
 
118
            return False;
 
119
        else:
 
120
            raise BzrError("lstat/stat of (%r): %r" % (f, e))
164
121
 
165
122
def fancy_rename(old, new, rename_func, unlink_func):
166
123
    """A fancy rename, when you don't have atomic rename.
216
173
            else:
217
174
                rename_func(tmp_name, new)
218
175
 
219
 
 
220
 
# In Python 2.4.2 and older, os.path.abspath and os.path.realpath
221
 
# choke on a Unicode string containing a relative path if
222
 
# os.getcwd() returns a non-sys.getdefaultencoding()-encoded
223
 
# string.
224
 
_fs_enc = sys.getfilesystemencoding()
225
 
def _posix_abspath(path):
226
 
    # jam 20060426 rather than encoding to fsencoding
227
 
    # copy posixpath.abspath, but use os.getcwdu instead
228
 
    if not posixpath.isabs(path):
229
 
        path = posixpath.join(getcwd(), path)
230
 
    return posixpath.normpath(path)
231
 
 
232
 
 
233
 
def _posix_realpath(path):
234
 
    return posixpath.realpath(path.encode(_fs_enc)).decode(_fs_enc)
235
 
 
236
 
 
237
 
def _win32_fixdrive(path):
238
 
    """Force drive letters to be consistent.
239
 
 
240
 
    win32 is inconsistent whether it returns lower or upper case
241
 
    and even if it was consistent the user might type the other
242
 
    so we force it to uppercase
243
 
    running python.exe under cmd.exe return capital C:\\
244
 
    running win32 python inside a cygwin shell returns lowercase c:\\
245
 
    """
246
 
    drive, path = _nt_splitdrive(path)
247
 
    return drive.upper() + path
248
 
 
249
 
 
250
 
def _win32_abspath(path):
251
 
    # Real _nt_abspath doesn't have a problem with a unicode cwd
252
 
    return _win32_fixdrive(_nt_abspath(unicode(path)).replace('\\', '/'))
253
 
 
254
 
 
255
 
def _win32_realpath(path):
256
 
    # Real _nt_realpath doesn't have a problem with a unicode cwd
257
 
    return _win32_fixdrive(_nt_realpath(unicode(path)).replace('\\', '/'))
258
 
 
259
 
 
260
 
def _win32_pathjoin(*args):
261
 
    return _nt_join(*args).replace('\\', '/')
262
 
 
263
 
 
264
 
def _win32_normpath(path):
265
 
    return _win32_fixdrive(_nt_normpath(unicode(path)).replace('\\', '/'))
266
 
 
267
 
 
268
 
def _win32_getcwd():
269
 
    return _win32_fixdrive(os.getcwdu().replace('\\', '/'))
270
 
 
271
 
 
272
 
def _win32_mkdtemp(*args, **kwargs):
273
 
    return _win32_fixdrive(tempfile.mkdtemp(*args, **kwargs).replace('\\', '/'))
274
 
 
275
 
 
276
 
def _win32_rename(old, new):
277
 
    """We expect to be able to atomically replace 'new' with old.
278
 
 
279
 
    On win32, if new exists, it must be moved out of the way first,
280
 
    and then deleted. 
281
 
    """
282
 
    try:
283
 
        fancy_rename(old, new, rename_func=os.rename, unlink_func=os.unlink)
284
 
    except OSError, e:
285
 
        if e.errno in (errno.EPERM, errno.EACCES, errno.EBUSY, errno.EINVAL):
286
 
            # If we try to rename a non-existant file onto cwd, we get 
287
 
            # EPERM or EACCES instead of ENOENT, this will raise ENOENT 
288
 
            # if the old path doesn't exist, sometimes we get EACCES
289
 
            # On Linux, we seem to get EBUSY, on Mac we get EINVAL
290
 
            os.lstat(old)
291
 
        raise
292
 
 
293
 
 
294
 
def _mac_getcwd():
295
 
    return unicodedata.normalize('NFKC', os.getcwdu())
296
 
 
297
 
 
298
176
# Default is to just use the python builtins, but these can be rebound on
299
177
# particular platforms.
300
 
abspath = _posix_abspath
301
 
realpath = _posix_realpath
 
178
abspath = os.path.abspath
 
179
realpath = os.path.realpath
302
180
pathjoin = os.path.join
303
181
normpath = os.path.normpath
304
182
getcwd = os.getcwdu
310
188
 
311
189
MIN_ABS_PATHLENGTH = 1
312
190
 
 
191
if os.name == "posix":
 
192
    # In Python 2.4.2 and older, os.path.abspath and os.path.realpath
 
193
    # choke on a Unicode string containing a relative path if
 
194
    # os.getcwd() returns a non-sys.getdefaultencoding()-encoded
 
195
    # string.
 
196
    _fs_enc = sys.getfilesystemencoding()
 
197
    def abspath(path):
 
198
        return os.path.abspath(path.encode(_fs_enc)).decode(_fs_enc)
 
199
 
 
200
    def realpath(path):
 
201
        return os.path.realpath(path.encode(_fs_enc)).decode(_fs_enc)
313
202
 
314
203
if sys.platform == 'win32':
315
 
    abspath = _win32_abspath
316
 
    realpath = _win32_realpath
317
 
    pathjoin = _win32_pathjoin
318
 
    normpath = _win32_normpath
319
 
    getcwd = _win32_getcwd
320
 
    mkdtemp = _win32_mkdtemp
321
 
    rename = _win32_rename
 
204
    # We need to use the Unicode-aware os.path.abspath and
 
205
    # os.path.realpath on Windows systems.
 
206
    def abspath(path):
 
207
        return os.path.abspath(path).replace('\\', '/')
 
208
 
 
209
    def realpath(path):
 
210
        return os.path.realpath(path).replace('\\', '/')
 
211
 
 
212
    def pathjoin(*args):
 
213
        return os.path.join(*args).replace('\\', '/')
 
214
 
 
215
    def normpath(path):
 
216
        return os.path.normpath(path).replace('\\', '/')
 
217
 
 
218
    def getcwd():
 
219
        return os.getcwdu().replace('\\', '/')
 
220
 
 
221
    def mkdtemp(*args, **kwargs):
 
222
        return tempfile.mkdtemp(*args, **kwargs).replace('\\', '/')
 
223
 
 
224
    def rename(old, new):
 
225
        fancy_rename(old, new, rename_func=os.rename, unlink_func=os.unlink)
322
226
 
323
227
    MIN_ABS_PATHLENGTH = 3
324
228
 
338
242
    def rmtree(path, ignore_errors=False, onerror=_win32_delete_readonly):
339
243
        """Replacer for shutil.rmtree: could remove readonly dirs/files"""
340
244
        return shutil.rmtree(path, ignore_errors, onerror)
341
 
elif sys.platform == 'darwin':
342
 
    getcwd = _mac_getcwd
343
 
 
344
 
 
345
 
def get_terminal_encoding():
346
 
    """Find the best encoding for printing to the screen.
347
 
 
348
 
    This attempts to check both sys.stdout and sys.stdin to see
349
 
    what encoding they are in, and if that fails it falls back to
350
 
    bzrlib.user_encoding.
351
 
    The problem is that on Windows, locale.getpreferredencoding()
352
 
    is not the same encoding as that used by the console:
353
 
    http://mail.python.org/pipermail/python-list/2003-May/162357.html
354
 
 
355
 
    On my standard US Windows XP, the preferred encoding is
356
 
    cp1252, but the console is cp437
357
 
    """
358
 
    output_encoding = getattr(sys.stdout, 'encoding', None)
359
 
    if not output_encoding:
360
 
        input_encoding = getattr(sys.stdin, 'encoding', None)
361
 
        if not input_encoding:
362
 
            output_encoding = bzrlib.user_encoding
363
 
            mutter('encoding stdout as bzrlib.user_encoding %r', output_encoding)
364
 
        else:
365
 
            output_encoding = input_encoding
366
 
            mutter('encoding stdout as sys.stdin encoding %r', output_encoding)
367
 
    else:
368
 
        mutter('encoding stdout as sys.stdout encoding %r', output_encoding)
369
 
    return output_encoding
370
245
 
371
246
 
372
247
def normalizepath(f):
477
352
        return False
478
353
 
479
354
 
480
 
def is_inside_or_parent_of_any(dir_list, fname):
481
 
    """True if fname is a child or a parent of any of the given files."""
482
 
    for dirname in dir_list:
483
 
        if is_inside(dirname, fname) or is_inside(fname, dirname):
484
 
            return True
485
 
    else:
486
 
        return False
487
 
 
488
 
 
489
355
def pumpfile(fromfile, tofile):
490
356
    """Copy contents of one file to another."""
491
357
    BUFSIZE = 32768
681
547
    return pathjoin(*p)
682
548
 
683
549
 
684
 
@deprecated_function(zero_nine)
685
550
def appendpath(p1, p2):
686
551
    if p1 == '':
687
552
        return p2
764
629
    assert len(base) >= MIN_ABS_PATHLENGTH, ('Length of base must be equal or'
765
630
        ' exceed the platform minimum length (which is %d)' % 
766
631
        MIN_ABS_PATHLENGTH)
767
 
 
768
632
    rp = abspath(path)
769
633
 
770
634
    s = []
776
640
        if tail:
777
641
            s.insert(0, tail)
778
642
    else:
 
643
        # XXX This should raise a NotChildPath exception, as its not tied
 
644
        # to branch anymore.
779
645
        raise PathNotChild(rp, base)
780
646
 
781
647
    if s:
800
666
        raise BzrBadParameterNotUnicode(unicode_or_utf8_string)
801
667
 
802
668
 
803
 
_platform_normalizes_filenames = False
804
 
if sys.platform == 'darwin':
805
 
    _platform_normalizes_filenames = True
806
 
 
807
 
 
808
 
def normalizes_filenames():
809
 
    """Return True if this platform normalizes unicode filenames.
810
 
 
811
 
    Mac OSX does, Windows/Linux do not.
812
 
    """
813
 
    return _platform_normalizes_filenames
814
 
 
815
 
 
816
 
def _accessible_normalized_filename(path):
817
 
    """Get the unicode normalized path, and if you can access the file.
818
 
 
819
 
    On platforms where the system normalizes filenames (Mac OSX),
820
 
    you can access a file by any path which will normalize correctly.
821
 
    On platforms where the system does not normalize filenames 
822
 
    (Windows, Linux), you have to access a file by its exact path.
823
 
 
824
 
    Internally, bzr only supports NFC/NFKC normalization, since that is 
825
 
    the standard for XML documents.
826
 
 
827
 
    So return the normalized path, and a flag indicating if the file
828
 
    can be accessed by that path.
829
 
    """
830
 
 
831
 
    return unicodedata.normalize('NFKC', unicode(path)), True
832
 
 
833
 
 
834
 
def _inaccessible_normalized_filename(path):
835
 
    __doc__ = _accessible_normalized_filename.__doc__
836
 
 
837
 
    normalized = unicodedata.normalize('NFKC', unicode(path))
838
 
    return normalized, normalized == path
839
 
 
840
 
 
841
 
if _platform_normalizes_filenames:
842
 
    normalized_filename = _accessible_normalized_filename
843
 
else:
844
 
    normalized_filename = _inaccessible_normalized_filename
845
 
 
846
 
 
847
669
def terminal_width():
848
670
    """Return estimated terminal width."""
849
671
    if sys.platform == 'win32':
871
693
    return sys.platform != "win32"
872
694
 
873
695
 
 
696
def strip_trailing_slash(path):
 
697
    """Strip trailing slash, except for root paths.
 
698
    The definition of 'root path' is platform-dependent.
 
699
    """
 
700
    if len(path) != MIN_ABS_PATHLENGTH and path[-1] == '/':
 
701
        return path[:-1]
 
702
    else:
 
703
        return path
 
704
 
 
705
 
874
706
_validWin32PathRE = re.compile(r'^([A-Za-z]:[/\\])?[^:<>*"?\|]*$')
875
707
 
876
708
 
883
715
        return
884
716
    if _validWin32PathRE.match(path) is None:
885
717
        raise IllegalPath(path)
886
 
 
887
 
 
888
 
def walkdirs(top, prefix=""):
889
 
    """Yield data about all the directories in a tree.
890
 
    
891
 
    This yields all the data about the contents of a directory at a time.
892
 
    After each directory has been yielded, if the caller has mutated the list
893
 
    to exclude some directories, they are then not descended into.
894
 
    
895
 
    The data yielded is of the form:
896
 
    ((directory-relpath, directory-path-from-top),
897
 
    [(relpath, basename, kind, lstat), ...]),
898
 
     - directory-relpath is the relative path of the directory being returned
899
 
       with respect to top. prefix is prepended to this.
900
 
     - directory-path-from-root is the path including top for this directory. 
901
 
       It is suitable for use with os functions.
902
 
     - relpath is the relative path within the subtree being walked.
903
 
     - basename is the basename of the path
904
 
     - kind is the kind of the file now. If unknown then the file is not
905
 
       present within the tree - but it may be recorded as versioned. See
906
 
       versioned_kind.
907
 
     - lstat is the stat data *if* the file was statted.
908
 
     - planned, not implemented: 
909
 
       path_from_tree_root is the path from the root of the tree.
910
 
 
911
 
    :param prefix: Prefix the relpaths that are yielded with 'prefix'. This 
912
 
        allows one to walk a subtree but get paths that are relative to a tree
913
 
        rooted higher up.
914
 
    :return: an iterator over the dirs.
915
 
    """
916
 
    #TODO there is a bit of a smell where the results of the directory-
917
 
    # summary in this, and the path from the root, may not agree 
918
 
    # depending on top and prefix - i.e. ./foo and foo as a pair leads to
919
 
    # potentially confusing output. We should make this more robust - but
920
 
    # not at a speed cost. RBC 20060731
921
 
    lstat = os.lstat
922
 
    pending = []
923
 
    _directory = _directory_kind
924
 
    _listdir = listdir
925
 
    pending = [(prefix, "", _directory, None, top)]
926
 
    while pending:
927
 
        dirblock = []
928
 
        currentdir = pending.pop()
929
 
        # 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
930
 
        top = currentdir[4]
931
 
        if currentdir[0]:
932
 
            relroot = currentdir[0] + '/'
933
 
        else:
934
 
            relroot = ""
935
 
        for name in sorted(_listdir(top)):
936
 
            abspath = top + '/' + name
937
 
            statvalue = lstat(abspath)
938
 
            dirblock.append((relroot + name, name,
939
 
                file_kind_from_stat_mode(statvalue.st_mode),
940
 
                statvalue, abspath))
941
 
        yield (currentdir[0], top), dirblock
942
 
        # push the user specified dirs from dirblock
943
 
        for dir in reversed(dirblock):
944
 
            if dir[2] == _directory:
945
 
                pending.append(dir)
946
 
 
947
 
 
948
 
def path_prefix_key(path):
949
 
    """Generate a prefix-order path key for path.
950
 
 
951
 
    This can be used to sort paths in the same way that walkdirs does.
952
 
    """
953
 
    return (dirname(path) , path)
954
 
 
955
 
 
956
 
def compare_paths_prefix_order(path_a, path_b):
957
 
    """Compare path_a and path_b to generate the same order walkdirs uses."""
958
 
    key_a = path_prefix_key(path_a)
959
 
    key_b = path_prefix_key(path_b)
960
 
    return cmp(key_a, key_b)