651
649
:param timezone: How to display the time: 'utc', 'original' for the
652
650
timezone specified by offset, or 'local' for the process's current
654
:param show_offset: Whether to append the timezone.
655
:param date_fmt: strftime format.
652
:param date_fmt: strftime format.
653
:param show_offset: Whether to append the timezone.
655
(date_fmt, tt, offset_str) = \
656
_format_date(t, offset, timezone, date_fmt, show_offset)
657
date_fmt = date_fmt.replace('%a', weekdays[tt[6]])
658
date_str = time.strftime(date_fmt, tt)
659
return date_str + offset_str
661
def format_local_date(t, offset=0, timezone='original', date_fmt=None,
663
"""Return an unicode date string formatted according to the current locale.
665
:param t: Seconds since the epoch.
666
:param offset: Timezone offset in seconds east of utc.
667
:param timezone: How to display the time: 'utc', 'original' for the
668
timezone specified by offset, or 'local' for the process's current
670
:param date_fmt: strftime format.
671
:param show_offset: Whether to append the timezone.
673
(date_fmt, tt, offset_str) = \
674
_format_date(t, offset, timezone, date_fmt, show_offset)
675
date_str = time.strftime(date_fmt, tt)
676
if not isinstance(date_str, unicode):
677
date_str = date_str.decode(bzrlib.user_encoding, 'replace')
678
return date_str + offset_str
680
def _format_date(t, offset, timezone, date_fmt, show_offset):
657
681
if timezone == 'utc':
658
682
tt = time.gmtime(t)
1110
1139
raise errors.IllegalPath(path)
1142
_WIN32_ERROR_DIRECTORY = 267 # Similar to errno.ENOTDIR
1144
def _is_error_enotdir(e):
1145
"""Check if this exception represents ENOTDIR.
1147
Unfortunately, python is very inconsistent about the exception
1148
here. The cases are:
1149
1) Linux, Mac OSX all versions seem to set errno == ENOTDIR
1150
2) Windows, Python2.4, uses errno == ERROR_DIRECTORY (267)
1151
which is the windows error code.
1152
3) Windows, Python2.5 uses errno == EINVAL and
1153
winerror == ERROR_DIRECTORY
1155
:param e: An Exception object (expected to be OSError with an errno
1156
attribute, but we should be able to cope with anything)
1157
:return: True if this represents an ENOTDIR error. False otherwise.
1159
en = getattr(e, 'errno', None)
1160
if (en == errno.ENOTDIR
1161
or (sys.platform == 'win32'
1162
and (en == _WIN32_ERROR_DIRECTORY
1163
or (en == errno.EINVAL
1164
and getattr(e, 'winerror', None) == _WIN32_ERROR_DIRECTORY)
1113
1170
def walkdirs(top, prefix=""):
1114
1171
"""Yield data about all the directories in a tree.
1161
1218
append = dirblock.append
1162
for name in sorted(_listdir(top)):
1163
abspath = top_slash + name
1164
statvalue = _lstat(abspath)
1165
kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
1166
append((relprefix + name, name, kind, statvalue, abspath))
1220
names = sorted(_listdir(top))
1222
if not _is_error_enotdir(e):
1226
abspath = top_slash + name
1227
statvalue = _lstat(abspath)
1228
kind = _kind_from_mode(statvalue.st_mode)
1229
append((relprefix + name, name, kind, statvalue, abspath))
1167
1230
yield (relroot, top), dirblock
1169
1232
# push the user specified dirs from dirblock
1170
1233
pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1236
class DirReader(object):
1237
"""An interface for reading directories."""
1239
def top_prefix_to_starting_dir(self, top, prefix=""):
1240
"""Converts top and prefix to a starting dir entry
1242
:param top: A utf8 path
1243
:param prefix: An optional utf8 path to prefix output relative paths
1245
:return: A tuple starting with prefix, and ending with the native
1248
raise NotImplementedError(self.top_prefix_to_starting_dir)
1250
def read_dir(self, prefix, top):
1251
"""Read a specific dir.
1253
:param prefix: A utf8 prefix to be preprended to the path basenames.
1254
:param top: A natively encoded path to read.
1255
:return: A list of the directories contents. Each item contains:
1256
(utf8_relpath, utf8_name, kind, lstatvalue, native_abspath)
1258
raise NotImplementedError(self.read_dir)
1261
_selected_dir_reader = None
1173
1264
def _walkdirs_utf8(top, prefix=""):
1174
1265
"""Yield data about all the directories in a tree.
1184
1275
path-from-top might be unicode or utf8, but it is the correct path to
1185
1276
pass to os functions to affect the file in question. (such as os.lstat)
1187
fs_encoding = _fs_enc.upper()
1188
if (sys.platform == 'win32' or
1189
fs_encoding not in ('UTF-8', 'US-ASCII', 'ANSI_X3.4-1968')): # ascii
1190
return _walkdirs_unicode_to_utf8(top, prefix=prefix)
1192
return _walkdirs_fs_utf8(top, prefix=prefix)
1195
def _walkdirs_fs_utf8(top, prefix=""):
1196
"""See _walkdirs_utf8.
1198
This sub-function is called when we know the filesystem is already in utf8
1199
encoding. So we don't need to transcode filenames.
1202
_directory = _directory_kind
1203
_listdir = os.listdir
1204
_kind_from_mode = _formats.get
1278
global _selected_dir_reader
1279
if _selected_dir_reader is None:
1280
fs_encoding = _fs_enc.upper()
1281
if sys.platform == "win32" and win32utils.winver == 'Windows NT':
1282
# Win98 doesn't have unicode apis like FindFirstFileW
1283
# TODO: We possibly could support Win98 by falling back to the
1284
# original FindFirstFile, and using TCHAR instead of WCHAR,
1285
# but that gets a bit tricky, and requires custom compiling
1288
from bzrlib._walkdirs_win32 import Win32ReadDir
1290
_selected_dir_reader = UnicodeDirReader()
1292
_selected_dir_reader = Win32ReadDir()
1293
elif fs_encoding not in ('UTF-8', 'US-ASCII', 'ANSI_X3.4-1968'):
1294
# ANSI_X3.4-1968 is a form of ASCII
1295
_selected_dir_reader = UnicodeDirReader()
1298
from bzrlib._readdir_pyx import UTF8DirReader
1300
# No optimised code path
1301
_selected_dir_reader = UnicodeDirReader()
1303
_selected_dir_reader = UTF8DirReader()
1206
1304
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
1207
1305
# But we don't actually uses 1-3 in pending, so set them to None
1208
pending = [(safe_utf8(prefix), None, None, None, safe_utf8(top))]
1306
pending = [[_selected_dir_reader.top_prefix_to_starting_dir(top, prefix)]]
1307
read_dir = _selected_dir_reader.read_dir
1308
_directory = _directory_kind
1210
relroot, _, _, _, top = pending.pop()
1212
relprefix = relroot + '/'
1215
top_slash = top + '/'
1218
append = dirblock.append
1219
for name in sorted(_listdir(top)):
1220
abspath = top_slash + name
1221
statvalue = _lstat(abspath)
1222
kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
1223
append((relprefix + name, name, kind, statvalue, abspath))
1310
relroot, _, _, _, top = pending[-1].pop()
1313
dirblock = sorted(read_dir(relroot, top))
1224
1314
yield (relroot, top), dirblock
1226
1315
# push the user specified dirs from dirblock
1227
pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1230
def _walkdirs_unicode_to_utf8(top, prefix=""):
1231
"""See _walkdirs_utf8
1233
Because Win32 has a Unicode api, all of the 'path-from-top' entries will be
1235
This is currently the fallback code path when the filesystem encoding is
1236
not UTF-8. It may be better to implement an alternative so that we can
1237
safely handle paths that are not properly decodable in the current
1240
_utf8_encode = codecs.getencoder('utf8')
1242
_directory = _directory_kind
1243
_listdir = os.listdir
1244
_kind_from_mode = _formats.get
1246
pending = [(safe_utf8(prefix), None, None, None, safe_unicode(top))]
1248
relroot, _, _, _, top = pending.pop()
1250
relprefix = relroot + '/'
1316
next = [d for d in reversed(dirblock) if d[2] == _directory]
1318
pending.append(next)
1321
class UnicodeDirReader(DirReader):
1322
"""A dir reader for non-utf8 file systems, which transcodes."""
1324
__slots__ = ['_utf8_encode']
1327
self._utf8_encode = codecs.getencoder('utf8')
1329
def top_prefix_to_starting_dir(self, top, prefix=""):
1330
"""See DirReader.top_prefix_to_starting_dir."""
1331
return (safe_utf8(prefix), None, None, None, safe_unicode(top))
1333
def read_dir(self, prefix, top):
1334
"""Read a single directory from a non-utf8 file system.
1336
top, and the abspath element in the output are unicode, all other paths
1337
are utf8. Local disk IO is done via unicode calls to listdir etc.
1339
This is currently the fallback code path when the filesystem encoding is
1340
not UTF-8. It may be better to implement an alternative so that we can
1341
safely handle paths that are not properly decodable in the current
1344
See DirReader.read_dir for details.
1346
_utf8_encode = self._utf8_encode
1348
_listdir = os.listdir
1349
_kind_from_mode = file_kind_from_stat_mode
1352
relprefix = prefix + '/'
1253
1355
top_slash = top + u'/'
1256
1358
append = dirblock.append
1257
1359
for name in sorted(_listdir(top)):
1258
name_utf8 = _utf8_encode(name)[0]
1361
name_utf8 = _utf8_encode(name)[0]
1362
except UnicodeDecodeError:
1363
raise errors.BadFilenameEncoding(
1364
_utf8_encode(relprefix)[0] + name, _fs_enc)
1259
1365
abspath = top_slash + name
1260
1366
statvalue = _lstat(abspath)
1261
kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
1367
kind = _kind_from_mode(statvalue.st_mode)
1262
1368
append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
1263
yield (relroot, top), dirblock
1265
# push the user specified dirs from dirblock
1266
pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1269
1372
def copy_tree(from_path, to_path, handlers={}):
1465
1592
base = abspath(pathjoin(base, '..', '..'))
1466
1593
filename = pathjoin(base, resource_relpath)
1467
1594
return open(filename, 'rU').read()
1597
def file_kind_from_stat_mode_thunk(mode):
1598
global file_kind_from_stat_mode
1599
if file_kind_from_stat_mode is file_kind_from_stat_mode_thunk:
1601
from bzrlib._readdir_pyx import UTF8DirReader
1602
file_kind_from_stat_mode = UTF8DirReader().kind_from_mode
1604
from bzrlib._readdir_py import (
1605
_kind_from_mode as file_kind_from_stat_mode
1607
return file_kind_from_stat_mode(mode)
1608
file_kind_from_stat_mode = file_kind_from_stat_mode_thunk
1611
def file_kind(f, _lstat=os.lstat):
1613
return file_kind_from_stat_mode(_lstat(f).st_mode)
1615
if getattr(e, 'errno', None) in (errno.ENOENT, errno.ENOTDIR):
1616
raise errors.NoSuchFile(f)