/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

Merge from bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
47
47
import unicodedata
48
48
 
49
49
from bzrlib import (
 
50
    cache_utf8,
50
51
    errors,
51
52
    win32utils,
52
53
    )
53
54
""")
54
55
 
55
56
import bzrlib
 
57
from bzrlib import symbol_versioning
56
58
from bzrlib.symbol_versioning import (
57
59
    deprecated_function,
58
60
    zero_nine,
747
749
    return rps
748
750
 
749
751
def joinpath(p):
750
 
    assert isinstance(p, list)
 
752
    assert isinstance(p, (list, tuple))
751
753
    for f in p:
752
754
        if (f == '..') or (f is None) or (f == ''):
753
755
            raise errors.BzrError("sorry, %r not allowed in path" % f)
813
815
    # 2) Isn't one of ' \t\r\n' which are characters we sometimes use as
814
816
    #    separators
815
817
    # 3) '\xa0' isn't unicode safe since it is >128.
816
 
    # So we are following textwrap's example and hard-coding our own.
817
 
    # We probably could ignore \v and \f, too.
818
 
    for ch in u' \t\n\r\v\f':
 
818
 
 
819
    # This should *not* be a unicode set of characters in case the source
 
820
    # string is not a Unicode string. We can auto-up-cast the characters since
 
821
    # they are ascii, but we don't want to auto-up-cast the string in case it
 
822
    # is utf-8
 
823
    for ch in ' \t\n\r\v\f':
819
824
        if ch in s:
820
825
            return True
821
826
    else:
881
886
        raise errors.BzrBadParameterNotUnicode(unicode_or_utf8_string)
882
887
 
883
888
 
 
889
def safe_utf8(unicode_or_utf8_string):
 
890
    """Coerce unicode_or_utf8_string to a utf8 string.
 
891
 
 
892
    If it is a str, it is returned.
 
893
    If it is Unicode, it is encoded into a utf-8 string.
 
894
    """
 
895
    if isinstance(unicode_or_utf8_string, str):
 
896
        # TODO: jam 20070209 This is overkill, and probably has an impact on
 
897
        #       performance if we are dealing with lots of apis that want a
 
898
        #       utf-8 revision id
 
899
        try:
 
900
            # Make sure it is a valid utf-8 string
 
901
            unicode_or_utf8_string.decode('utf-8')
 
902
        except UnicodeDecodeError:
 
903
            raise errors.BzrBadParameterNotUnicode(unicode_or_utf8_string)
 
904
        return unicode_or_utf8_string
 
905
    return unicode_or_utf8_string.encode('utf-8')
 
906
 
 
907
 
 
908
_revision_id_warning = ('Unicode revision ids were deprecated in bzr 0.15.'
 
909
                        ' Revision id generators should be creating utf8'
 
910
                        ' revision ids.')
 
911
 
 
912
 
 
913
def safe_revision_id(unicode_or_utf8_string, warn=True):
 
914
    """Revision ids should now be utf8, but at one point they were unicode.
 
915
 
 
916
    :param unicode_or_utf8_string: A possibly Unicode revision_id. (can also be
 
917
        utf8 or None).
 
918
    :param warn: Functions that are sanitizing user data can set warn=False
 
919
    :return: None or a utf8 revision id.
 
920
    """
 
921
    if (unicode_or_utf8_string is None
 
922
        or unicode_or_utf8_string.__class__ == str):
 
923
        return unicode_or_utf8_string
 
924
    if warn:
 
925
        symbol_versioning.warn(_revision_id_warning, DeprecationWarning,
 
926
                               stacklevel=2)
 
927
    return cache_utf8.encode(unicode_or_utf8_string)
 
928
 
 
929
 
 
930
_file_id_warning = ('Unicode file ids were deprecated in bzr 0.15. File id'
 
931
                    ' generators should be creating utf8 file ids.')
 
932
 
 
933
 
 
934
def safe_file_id(unicode_or_utf8_string, warn=True):
 
935
    """File ids should now be utf8, but at one point they were unicode.
 
936
 
 
937
    This is the same as safe_utf8, except it uses the cached encode functions
 
938
    to save a little bit of performance.
 
939
 
 
940
    :param unicode_or_utf8_string: A possibly Unicode file_id. (can also be
 
941
        utf8 or None).
 
942
    :param warn: Functions that are sanitizing user data can set warn=False
 
943
    :return: None or a utf8 file id.
 
944
    """
 
945
    if (unicode_or_utf8_string is None
 
946
        or unicode_or_utf8_string.__class__ == str):
 
947
        return unicode_or_utf8_string
 
948
    if warn:
 
949
        symbol_versioning.warn(_file_id_warning, DeprecationWarning,
 
950
                               stacklevel=2)
 
951
    return cache_utf8.encode(unicode_or_utf8_string)
 
952
 
 
953
 
884
954
_platform_normalizes_filenames = False
885
955
if sys.platform == 'darwin':
886
956
    _platform_normalizes_filenames = True
1007
1077
    
1008
1078
    The data yielded is of the form:
1009
1079
    ((directory-relpath, directory-path-from-top),
1010
 
    [(relpath, basename, kind, lstat), ...]),
 
1080
    [(directory-relpath, basename, kind, lstat, path-from-top), ...]),
1011
1081
     - directory-relpath is the relative path of the directory being returned
1012
1082
       with respect to top. prefix is prepended to this.
1013
1083
     - directory-path-from-root is the path including top for this directory. 
1031
1101
    # depending on top and prefix - i.e. ./foo and foo as a pair leads to
1032
1102
    # potentially confusing output. We should make this more robust - but
1033
1103
    # not at a speed cost. RBC 20060731
1034
 
    lstat = os.lstat
1035
 
    pending = []
 
1104
    _lstat = os.lstat
1036
1105
    _directory = _directory_kind
1037
1106
    _listdir = os.listdir
1038
 
    pending = [(prefix, "", _directory, None, top)]
 
1107
    _kind_from_mode = _formats.get
 
1108
    pending = [(safe_unicode(prefix), "", _directory, None, safe_unicode(top))]
1039
1109
    while pending:
1040
 
        dirblock = []
1041
 
        currentdir = pending.pop()
1042
1110
        # 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
1043
 
        top = currentdir[4]
1044
 
        if currentdir[0]:
1045
 
            relroot = currentdir[0] + '/'
1046
 
        else:
1047
 
            relroot = ""
1048
 
        for name in sorted(_listdir(top)):
1049
 
            abspath = top + '/' + name
1050
 
            statvalue = lstat(abspath)
1051
 
            dirblock.append((relroot + name, name,
1052
 
                file_kind_from_stat_mode(statvalue.st_mode),
1053
 
                statvalue, abspath))
1054
 
        yield (currentdir[0], top), dirblock
1055
 
        # push the user specified dirs from dirblock
1056
 
        for dir in reversed(dirblock):
1057
 
            if dir[2] == _directory:
1058
 
                pending.append(dir)
 
1111
        relroot, _, _, _, top = pending.pop()
 
1112
        if relroot:
 
1113
            relprefix = relroot + u'/'
 
1114
        else:
 
1115
            relprefix = ''
 
1116
        top_slash = top + u'/'
 
1117
 
 
1118
        dirblock = []
 
1119
        append = dirblock.append
 
1120
        for name in sorted(_listdir(top)):
 
1121
            abspath = top_slash + name
 
1122
            statvalue = _lstat(abspath)
 
1123
            kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
 
1124
            append((relprefix + name, name, kind, statvalue, abspath))
 
1125
        yield (relroot, top), dirblock
 
1126
 
 
1127
        # push the user specified dirs from dirblock
 
1128
        pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
 
1129
 
 
1130
 
 
1131
def _walkdirs_utf8(top, prefix=""):
 
1132
    """Yield data about all the directories in a tree.
 
1133
 
 
1134
    This yields the same information as walkdirs() only each entry is yielded
 
1135
    in utf-8. On platforms which have a filesystem encoding of utf8 the paths
 
1136
    are returned as exact byte-strings.
 
1137
 
 
1138
    :return: yields a tuple of (dir_info, [file_info])
 
1139
        dir_info is (utf8_relpath, path-from-top)
 
1140
        file_info is (utf8_relpath, utf8_name, kind, lstat, path-from-top)
 
1141
        if top is an absolute path, path-from-top is also an absolute path.
 
1142
        path-from-top might be unicode or utf8, but it is the correct path to
 
1143
        pass to os functions to affect the file in question. (such as os.lstat)
 
1144
    """
 
1145
    fs_encoding = sys.getfilesystemencoding()
 
1146
    if (sys.platform == 'win32' or
 
1147
        fs_encoding not in ('UTF-8', 'US-ASCII', 'ANSI_X3.4-1968')): # ascii
 
1148
        return _walkdirs_unicode_to_utf8(top, prefix=prefix)
 
1149
    else:
 
1150
        return _walkdirs_fs_utf8(top, prefix=prefix)
 
1151
 
 
1152
 
 
1153
def _walkdirs_fs_utf8(top, prefix=""):
 
1154
    """See _walkdirs_utf8.
 
1155
 
 
1156
    This sub-function is called when we know the filesystem is already in utf8
 
1157
    encoding. So we don't need to transcode filenames.
 
1158
    """
 
1159
    _lstat = os.lstat
 
1160
    _directory = _directory_kind
 
1161
    _listdir = os.listdir
 
1162
    _kind_from_mode = _formats.get
 
1163
 
 
1164
    # 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
 
1165
    # But we don't actually uses 1-3 in pending, so set them to None
 
1166
    pending = [(safe_utf8(prefix), None, None, None, safe_utf8(top))]
 
1167
    while pending:
 
1168
        relroot, _, _, _, top = pending.pop()
 
1169
        if relroot:
 
1170
            relprefix = relroot + '/'
 
1171
        else:
 
1172
            relprefix = ''
 
1173
        top_slash = top + '/'
 
1174
 
 
1175
        dirblock = []
 
1176
        append = dirblock.append
 
1177
        for name in sorted(_listdir(top)):
 
1178
            abspath = top_slash + name
 
1179
            statvalue = _lstat(abspath)
 
1180
            kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
 
1181
            append((relprefix + name, name, kind, statvalue, abspath))
 
1182
        yield (relroot, top), dirblock
 
1183
 
 
1184
        # push the user specified dirs from dirblock
 
1185
        pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
 
1186
 
 
1187
 
 
1188
def _walkdirs_unicode_to_utf8(top, prefix=""):
 
1189
    """See _walkdirs_utf8
 
1190
 
 
1191
    Because Win32 has a Unicode api, all of the 'path-from-top' entries will be
 
1192
    Unicode paths.
 
1193
    This is currently the fallback code path when the filesystem encoding is
 
1194
    not UTF-8. It may be better to implement an alternative so that we can
 
1195
    safely handle paths that are not properly decodable in the current
 
1196
    encoding.
 
1197
    """
 
1198
    _utf8_encode = codecs.getencoder('utf8')
 
1199
    _lstat = os.lstat
 
1200
    _directory = _directory_kind
 
1201
    _listdir = os.listdir
 
1202
    _kind_from_mode = _formats.get
 
1203
 
 
1204
    pending = [(safe_utf8(prefix), None, None, None, safe_unicode(top))]
 
1205
    while pending:
 
1206
        relroot, _, _, _, top = pending.pop()
 
1207
        if relroot:
 
1208
            relprefix = relroot + '/'
 
1209
        else:
 
1210
            relprefix = ''
 
1211
        top_slash = top + u'/'
 
1212
 
 
1213
        dirblock = []
 
1214
        append = dirblock.append
 
1215
        for name in sorted(_listdir(top)):
 
1216
            name_utf8 = _utf8_encode(name)[0]
 
1217
            abspath = top_slash + name
 
1218
            statvalue = _lstat(abspath)
 
1219
            kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
 
1220
            append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
 
1221
        yield (relroot, top), dirblock
 
1222
 
 
1223
        # push the user specified dirs from dirblock
 
1224
        pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1059
1225
 
1060
1226
 
1061
1227
def copy_tree(from_path, to_path, handlers={}):