~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-06-03 20:18:35 UTC
  • mfrom: (1185.82.137 w-changeset)
  • Revision ID: pqm@pqm.ubuntu.com-20060603201835-1c9a1725641ccd24
Implement bundles

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
28
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
29
import string
38
30
import sys
39
31
import time
40
32
import types
41
33
import tempfile
42
 
import unicodedata
43
34
 
44
35
import bzrlib
45
36
from bzrlib.errors import (BzrError,
48
39
                           PathNotChild,
49
40
                           IllegalPath,
50
41
                           )
51
 
from bzrlib.symbol_versioning import (deprecated_function, 
52
 
        zero_nine)
 
42
from bzrlib.symbol_versioning import *
53
43
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)
 
44
import bzrlib.win32console
62
45
 
63
46
 
64
47
def make_readonly(filename):
93
76
        return f
94
77
 
95
78
 
96
 
_directory_kind = 'directory'
97
 
 
98
79
_formats = {
99
 
    stat.S_IFDIR:_directory_kind,
 
80
    stat.S_IFDIR:'directory',
100
81
    stat.S_IFCHR:'chardev',
101
82
    stat.S_IFBLK:'block',
102
83
    stat.S_IFREG:'file',
104
85
    stat.S_IFLNK:'symlink',
105
86
    stat.S_IFSOCK:'socket',
106
87
}
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
 
    """
 
88
def file_kind(f, _formats=_formats, _unknown='unknown', _lstat=os.lstat):
114
89
    try:
115
 
        return _formats[stat_mode & 0170000]
 
90
        return _formats[_lstat(f).st_mode & 0170000]
116
91
    except KeyError:
117
92
        return _unknown
118
93
 
119
94
 
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
137
 
 
138
 
 
139
95
def kind_marker(kind):
140
96
    if kind == 'file':
141
97
        return ''
142
 
    elif kind == _directory_kind:
 
98
    elif kind == 'directory':
143
99
        return '/'
144
100
    elif kind == 'symlink':
145
101
        return '@'
216
172
            else:
217
173
                rename_func(tmp_name, new)
218
174
 
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
175
# Default is to just use the python builtins, but these can be rebound on
299
176
# particular platforms.
300
 
abspath = _posix_abspath
301
 
realpath = _posix_realpath
 
177
abspath = os.path.abspath
 
178
realpath = os.path.realpath
302
179
pathjoin = os.path.join
303
180
normpath = os.path.normpath
304
181
getcwd = os.getcwdu
310
187
 
311
188
MIN_ABS_PATHLENGTH = 1
312
189
 
 
190
if os.name == "posix":
 
191
    # In Python 2.4.2 and older, os.path.abspath and os.path.realpath
 
192
    # choke on a Unicode string containing a relative path if
 
193
    # os.getcwd() returns a non-sys.getdefaultencoding()-encoded
 
194
    # string.
 
195
    _fs_enc = sys.getfilesystemencoding()
 
196
    def abspath(path):
 
197
        return os.path.abspath(path.encode(_fs_enc)).decode(_fs_enc)
 
198
 
 
199
    def realpath(path):
 
200
        return os.path.realpath(path.encode(_fs_enc)).decode(_fs_enc)
313
201
 
314
202
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
 
203
    # We need to use the Unicode-aware os.path.abspath and
 
204
    # os.path.realpath on Windows systems.
 
205
    def abspath(path):
 
206
        return os.path.abspath(path).replace('\\', '/')
 
207
 
 
208
    def realpath(path):
 
209
        return os.path.realpath(path).replace('\\', '/')
 
210
 
 
211
    def pathjoin(*args):
 
212
        return os.path.join(*args).replace('\\', '/')
 
213
 
 
214
    def normpath(path):
 
215
        return os.path.normpath(path).replace('\\', '/')
 
216
 
 
217
    def getcwd():
 
218
        return os.getcwdu().replace('\\', '/')
 
219
 
 
220
    def mkdtemp(*args, **kwargs):
 
221
        return tempfile.mkdtemp(*args, **kwargs).replace('\\', '/')
 
222
 
 
223
    def rename(old, new):
 
224
        fancy_rename(old, new, rename_func=os.rename, unlink_func=os.unlink)
322
225
 
323
226
    MIN_ABS_PATHLENGTH = 3
324
227
 
338
241
    def rmtree(path, ignore_errors=False, onerror=_win32_delete_readonly):
339
242
        """Replacer for shutil.rmtree: could remove readonly dirs/files"""
340
243
        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
244
 
371
245
 
372
246
def normalizepath(f):
477
351
        return False
478
352
 
479
353
 
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
354
def pumpfile(fromfile, tofile):
490
355
    """Copy contents of one file to another."""
491
356
    BUFSIZE = 32768
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)