653
649
:param timezone: How to display the time: 'utc', 'original' for the
654
650
timezone specified by offset, or 'local' for the process's current
656
:param show_offset: Whether to append the timezone.
657
: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):
659
681
if timezone == 'utc':
660
682
tt = time.gmtime(t)
1119
1160
raise errors.IllegalPath(path)
1163
_WIN32_ERROR_DIRECTORY = 267 # Similar to errno.ENOTDIR
1165
def _is_error_enotdir(e):
1166
"""Check if this exception represents ENOTDIR.
1168
Unfortunately, python is very inconsistent about the exception
1169
here. The cases are:
1170
1) Linux, Mac OSX all versions seem to set errno == ENOTDIR
1171
2) Windows, Python2.4, uses errno == ERROR_DIRECTORY (267)
1172
which is the windows error code.
1173
3) Windows, Python2.5 uses errno == EINVAL and
1174
winerror == ERROR_DIRECTORY
1176
:param e: An Exception object (expected to be OSError with an errno
1177
attribute, but we should be able to cope with anything)
1178
:return: True if this represents an ENOTDIR error. False otherwise.
1180
en = getattr(e, 'errno', None)
1181
if (en == errno.ENOTDIR
1182
or (sys.platform == 'win32'
1183
and (en == _WIN32_ERROR_DIRECTORY
1184
or (en == errno.EINVAL
1185
and getattr(e, 'winerror', None) == _WIN32_ERROR_DIRECTORY)
1122
1191
def walkdirs(top, prefix=""):
1123
1192
"""Yield data about all the directories in a tree.
1188
1254
pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1191
_real_walkdirs_utf8 = None
1257
class DirReader(object):
1258
"""An interface for reading directories."""
1260
def top_prefix_to_starting_dir(self, top, prefix=""):
1261
"""Converts top and prefix to a starting dir entry
1263
:param top: A utf8 path
1264
:param prefix: An optional utf8 path to prefix output relative paths
1266
:return: A tuple starting with prefix, and ending with the native
1269
raise NotImplementedError(self.top_prefix_to_starting_dir)
1271
def read_dir(self, prefix, top):
1272
"""Read a specific dir.
1274
:param prefix: A utf8 prefix to be preprended to the path basenames.
1275
:param top: A natively encoded path to read.
1276
:return: A list of the directories contents. Each item contains:
1277
(utf8_relpath, utf8_name, kind, lstatvalue, native_abspath)
1279
raise NotImplementedError(self.read_dir)
1282
_selected_dir_reader = None
1193
1285
def _walkdirs_utf8(top, prefix=""):
1194
1286
"""Yield data about all the directories in a tree.
1204
1296
path-from-top might be unicode or utf8, but it is the correct path to
1205
1297
pass to os functions to affect the file in question. (such as os.lstat)
1207
global _real_walkdirs_utf8
1208
if _real_walkdirs_utf8 is None:
1299
global _selected_dir_reader
1300
if _selected_dir_reader is None:
1209
1301
fs_encoding = _fs_enc.upper()
1210
if win32utils.winver == 'Windows NT':
1302
if sys.platform == "win32" and win32utils.winver == 'Windows NT':
1211
1303
# Win98 doesn't have unicode apis like FindFirstFileW
1212
1304
# TODO: We possibly could support Win98 by falling back to the
1213
1305
# original FindFirstFile, and using TCHAR instead of WCHAR,
1214
1306
# but that gets a bit tricky, and requires custom compiling
1215
1307
# for win98 anyway.
1217
from bzrlib._walkdirs_win32 import _walkdirs_utf8_win32_find_file
1309
from bzrlib._walkdirs_win32 import Win32ReadDir
1218
1310
except ImportError:
1219
_real_walkdirs_utf8 = _walkdirs_unicode_to_utf8
1311
_selected_dir_reader = UnicodeDirReader()
1221
_real_walkdirs_utf8 = _walkdirs_utf8_win32_find_file
1313
_selected_dir_reader = Win32ReadDir()
1222
1314
elif fs_encoding not in ('UTF-8', 'US-ASCII', 'ANSI_X3.4-1968'):
1223
1315
# ANSI_X3.4-1968 is a form of ASCII
1224
_real_walkdirs_utf8 = _walkdirs_unicode_to_utf8
1316
_selected_dir_reader = UnicodeDirReader()
1226
_real_walkdirs_utf8 = _walkdirs_fs_utf8
1227
return _real_walkdirs_utf8(top, prefix=prefix)
1230
def _walkdirs_fs_utf8(top, prefix=""):
1231
"""See _walkdirs_utf8.
1233
This sub-function is called when we know the filesystem is already in utf8
1234
encoding. So we don't need to transcode filenames.
1237
_directory = _directory_kind
1238
_listdir = os.listdir
1239
_kind_from_mode = _formats.get
1319
from bzrlib._readdir_pyx import UTF8DirReader
1321
# No optimised code path
1322
_selected_dir_reader = UnicodeDirReader()
1324
_selected_dir_reader = UTF8DirReader()
1241
1325
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
1242
1326
# But we don't actually uses 1-3 in pending, so set them to None
1243
pending = [(safe_utf8(prefix), None, None, None, safe_utf8(top))]
1327
pending = [[_selected_dir_reader.top_prefix_to_starting_dir(top, prefix)]]
1328
read_dir = _selected_dir_reader.read_dir
1329
_directory = _directory_kind
1245
relroot, _, _, _, top = pending.pop()
1247
relprefix = relroot + '/'
1250
top_slash = top + '/'
1253
append = dirblock.append
1254
for name in sorted(_listdir(top)):
1255
abspath = top_slash + name
1256
statvalue = _lstat(abspath)
1257
kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
1258
append((relprefix + name, name, kind, statvalue, abspath))
1331
relroot, _, _, _, top = pending[-1].pop()
1334
dirblock = sorted(read_dir(relroot, top))
1259
1335
yield (relroot, top), dirblock
1261
1336
# push the user specified dirs from dirblock
1262
pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1265
def _walkdirs_unicode_to_utf8(top, prefix=""):
1266
"""See _walkdirs_utf8
1268
Because Win32 has a Unicode api, all of the 'path-from-top' entries will be
1270
This is currently the fallback code path when the filesystem encoding is
1271
not UTF-8. It may be better to implement an alternative so that we can
1272
safely handle paths that are not properly decodable in the current
1275
_utf8_encode = codecs.getencoder('utf8')
1277
_directory = _directory_kind
1278
_listdir = os.listdir
1279
_kind_from_mode = _formats.get
1281
pending = [(safe_utf8(prefix), None, None, None, safe_unicode(top))]
1283
relroot, _, _, _, top = pending.pop()
1285
relprefix = relroot + '/'
1337
next = [d for d in reversed(dirblock) if d[2] == _directory]
1339
pending.append(next)
1342
class UnicodeDirReader(DirReader):
1343
"""A dir reader for non-utf8 file systems, which transcodes."""
1345
__slots__ = ['_utf8_encode']
1348
self._utf8_encode = codecs.getencoder('utf8')
1350
def top_prefix_to_starting_dir(self, top, prefix=""):
1351
"""See DirReader.top_prefix_to_starting_dir."""
1352
return (safe_utf8(prefix), None, None, None, safe_unicode(top))
1354
def read_dir(self, prefix, top):
1355
"""Read a single directory from a non-utf8 file system.
1357
top, and the abspath element in the output are unicode, all other paths
1358
are utf8. Local disk IO is done via unicode calls to listdir etc.
1360
This is currently the fallback code path when the filesystem encoding is
1361
not UTF-8. It may be better to implement an alternative so that we can
1362
safely handle paths that are not properly decodable in the current
1365
See DirReader.read_dir for details.
1367
_utf8_encode = self._utf8_encode
1369
_listdir = os.listdir
1370
_kind_from_mode = file_kind_from_stat_mode
1373
relprefix = prefix + '/'
1288
1376
top_slash = top + u'/'
1291
1379
append = dirblock.append
1292
1380
for name in sorted(_listdir(top)):
1293
name_utf8 = _utf8_encode(name)[0]
1382
name_utf8 = _utf8_encode(name)[0]
1383
except UnicodeDecodeError:
1384
raise errors.BadFilenameEncoding(
1385
_utf8_encode(relprefix)[0] + name, _fs_enc)
1294
1386
abspath = top_slash + name
1295
1387
statvalue = _lstat(abspath)
1296
kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
1388
kind = _kind_from_mode(statvalue.st_mode)
1297
1389
append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
1298
yield (relroot, top), dirblock
1300
# push the user specified dirs from dirblock
1301
pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1304
1393
def copy_tree(from_path, to_path, handlers={}):
1500
1613
base = abspath(pathjoin(base, '..', '..'))
1501
1614
filename = pathjoin(base, resource_relpath)
1502
1615
return open(filename, 'rU').read()
1618
def file_kind_from_stat_mode_thunk(mode):
1619
global file_kind_from_stat_mode
1620
if file_kind_from_stat_mode is file_kind_from_stat_mode_thunk:
1622
from bzrlib._readdir_pyx import UTF8DirReader
1623
file_kind_from_stat_mode = UTF8DirReader().kind_from_mode
1625
from bzrlib._readdir_py import (
1626
_kind_from_mode as file_kind_from_stat_mode
1628
return file_kind_from_stat_mode(mode)
1629
file_kind_from_stat_mode = file_kind_from_stat_mode_thunk
1632
def file_kind(f, _lstat=os.lstat):
1634
return file_kind_from_stat_mode(_lstat(f).st_mode)
1636
if getattr(e, 'errno', None) in (errno.ENOENT, errno.ENOTDIR):
1637
raise errors.NoSuchFile(f)
1640
if sys.platform == "win32":
1643
return msvcrt.getch()
1648
fd = sys.stdin.fileno()
1649
settings = termios.tcgetattr(fd)
1652
ch = sys.stdin.read(1)
1654
termios.tcsetattr(fd, termios.TCSADRAIN, settings)