124
124
_directory_kind = 'directory'
127
stat.S_IFDIR:_directory_kind,
128
stat.S_IFCHR:'chardev',
129
stat.S_IFBLK:'block',
132
stat.S_IFLNK:'symlink',
133
stat.S_IFSOCK:'socket',
137
def file_kind_from_stat_mode(stat_mode, _formats=_formats, _unknown='unknown'):
138
"""Generate a file kind from a stat mode. This is used in walkdirs.
140
Its performance is critical: Do not mutate without careful benchmarking.
143
return _formats[stat_mode & 0170000]
148
def file_kind(f, _lstat=os.lstat, _mapper=file_kind_from_stat_mode):
150
return _mapper(_lstat(f).st_mode)
152
if getattr(e, 'errno', None) in (errno.ENOENT, errno.ENOTDIR):
153
raise errors.NoSuchFile(f)
158
127
"""Return the current umask"""
159
128
# Assume that people aren't messing with the umask while running
1231
1200
pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1234
_real_walkdirs_utf8 = None
1203
class DirReader(object):
1204
"""An interface for reading directories."""
1206
def top_prefix_to_starting_dir(self, top, prefix=""):
1207
"""Converts top and prefix to a starting dir entry
1209
:param top: A utf8 path
1210
:param prefix: An optional utf8 path to prefix output relative paths
1212
:return: A tuple starting with prefix, and ending with the native
1215
raise NotImplementedError(self.top_prefix_to_starting_dir)
1217
def read_dir(self, prefix, top):
1218
"""Read a specific dir.
1220
:param prefix: A utf8 prefix to be preprended to the path basenames.
1221
:param top: A natively encoded path to read.
1222
:return: A list of the directories contents. Each item contains:
1223
(utf8_relpath, utf8_name, kind, lstatvalue, native_abspath)
1225
raise NotImplementedError(self.read_dir)
1228
_selected_dir_reader = None
1236
1231
def _walkdirs_utf8(top, prefix=""):
1237
1232
"""Yield data about all the directories in a tree.
1257
1252
# but that gets a bit tricky, and requires custom compiling
1258
1253
# for win98 anyway.
1260
from bzrlib._walkdirs_win32 import _walkdirs_utf8_win32_find_file
1255
from bzrlib._walkdirs_win32 import Win32ReadDir
1261
1256
except ImportError:
1262
_real_walkdirs_utf8 = _walkdirs_unicode_to_utf8
1257
_selected_dir_reader = UnicodeDirReader()
1264
_real_walkdirs_utf8 = _walkdirs_utf8_win32_find_file
1259
_selected_dir_reader = Win32ReadDir()
1265
1260
elif fs_encoding not in ('UTF-8', 'US-ASCII', 'ANSI_X3.4-1968'):
1266
1261
# ANSI_X3.4-1968 is a form of ASCII
1267
_real_walkdirs_utf8 = _walkdirs_unicode_to_utf8
1262
_selected_dir_reader = UnicodeDirReader()
1269
_real_walkdirs_utf8 = _walkdirs_fs_utf8
1270
return _real_walkdirs_utf8(top, prefix=prefix)
1273
def _walkdirs_fs_utf8(top, prefix=""):
1274
"""See _walkdirs_utf8.
1276
This sub-function is called when we know the filesystem is already in utf8
1277
encoding. So we don't need to transcode filenames.
1280
_directory = _directory_kind
1281
# Use C accelerated directory listing.
1282
_listdir = _read_dir
1283
_kind_from_mode = _formats.get
1265
from bzrlib._readdir_pyx import UTF8DirReader
1267
# No optimised code path
1268
_selected_dir_reader = UnicodeDirReader()
1270
_selected_dir_reader = UTF8DirReader()
1285
1271
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
1286
1272
# But we don't actually uses 1-3 in pending, so set them to None
1287
pending = [(safe_utf8(prefix), None, None, None, safe_utf8(top))]
1273
pending = [[_selected_dir_reader.top_prefix_to_starting_dir(top, prefix)]]
1274
read_dir = _selected_dir_reader.read_dir
1275
_directory = _directory_kind
1289
relroot, _, _, _, top = pending.pop()
1291
relprefix = relroot + '/'
1294
top_slash = top + '/'
1297
append = dirblock.append
1298
# read_dir supplies in should-stat order.
1299
for _, name in sorted(_listdir(top)):
1300
abspath = top_slash + name
1301
statvalue = _lstat(abspath)
1302
kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
1303
append((relprefix + name, name, kind, statvalue, abspath))
1277
relroot, _, _, _, top = pending[-1].pop()
1280
dirblock = sorted(read_dir(relroot, top))
1305
1281
yield (relroot, top), dirblock
1307
1282
# push the user specified dirs from dirblock
1308
pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1311
def _walkdirs_unicode_to_utf8(top, prefix=""):
1312
"""See _walkdirs_utf8
1314
Because Win32 has a Unicode api, all of the 'path-from-top' entries will be
1316
This is currently the fallback code path when the filesystem encoding is
1317
not UTF-8. It may be better to implement an alternative so that we can
1318
safely handle paths that are not properly decodable in the current
1321
_utf8_encode = codecs.getencoder('utf8')
1323
_directory = _directory_kind
1324
_listdir = os.listdir
1325
_kind_from_mode = _formats.get
1327
pending = [(safe_utf8(prefix), None, None, None, safe_unicode(top))]
1329
relroot, _, _, _, top = pending.pop()
1331
relprefix = relroot + '/'
1283
next = [d for d in reversed(dirblock) if d[2] == _directory]
1285
pending.append(next)
1288
class UnicodeDirReader(DirReader):
1289
"""A dir reader for non-utf8 file systems, which transcodes."""
1291
__slots__ = ['_utf8_encode']
1294
self._utf8_encode = codecs.getencoder('utf8')
1296
def top_prefix_to_starting_dir(self, top, prefix=""):
1297
"""See DirReader.top_prefix_to_starting_dir."""
1298
return (safe_utf8(prefix), None, None, None, safe_unicode(top))
1300
def read_dir(self, prefix, top):
1301
"""Read a single directory from a non-utf8 file system.
1303
top, and the abspath element in the output are unicode, all other paths
1304
are utf8. Local disk IO is done via unicode calls to listdir etc.
1306
This is currently the fallback code path when the filesystem encoding is
1307
not UTF-8. It may be better to implement an alternative so that we can
1308
safely handle paths that are not properly decodable in the current
1311
See DirReader.read_dir for details.
1313
_utf8_encode = self._utf8_encode
1315
_listdir = os.listdir
1316
_kind_from_mode = file_kind_from_stat_mode
1319
relprefix = prefix + '/'
1334
1322
top_slash = top + u'/'
1337
1325
append = dirblock.append
1338
1326
for name in sorted(_listdir(top)):
1339
name_utf8 = _utf8_encode(name)[0]
1328
name_utf8 = _utf8_encode(name)[0]
1329
except UnicodeDecodeError:
1330
raise errors.BadFilenameEncoding(
1331
_utf8_encode(relprefix)[0] + name, _fs_enc)
1340
1332
abspath = top_slash + name
1341
1333
statvalue = _lstat(abspath)
1342
kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
1334
kind = _kind_from_mode(statvalue.st_mode)
1343
1335
append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
1344
yield (relroot, top), dirblock
1346
# push the user specified dirs from dirblock
1347
pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1350
1339
def copy_tree(from_path, to_path, handlers={}):
1572
1561
return open(filename, 'rU').read()
1576
from bzrlib._readdir_pyx import read_dir as _read_dir
1578
from bzrlib._readdir_py import read_dir as _read_dir
1564
def file_kind_from_stat_mode_thunk(mode):
1565
global file_kind_from_stat_mode
1566
if file_kind_from_stat_mode is file_kind_from_stat_mode_thunk:
1568
from bzrlib._readdir_pyx import UTF8DirReader
1569
file_kind_from_stat_mode = UTF8DirReader().kind_from_mode
1571
from bzrlib._readdir_py import (
1572
_kind_from_mode as file_kind_from_stat_mode
1574
return file_kind_from_stat_mode(mode)
1575
file_kind_from_stat_mode = file_kind_from_stat_mode_thunk
1578
def file_kind(f, _lstat=os.lstat):
1580
return file_kind_from_stat_mode(_lstat(f).st_mode)
1582
if getattr(e, 'errno', None) in (errno.ENOENT, errno.ENOTDIR):
1583
raise errors.NoSuchFile(f)