1
# Copyright (C) 2005 by Canonical Development Ltd
1
# Copyright (C) 2005, 2006 by Canonical Development Ltd
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
34
34
from bzrlib.errors import BzrError, UnlistableStore, TransportNotPossible
35
35
from bzrlib.symbol_versioning import *
36
36
from bzrlib.trace import mutter
37
import bzrlib.transport as transport
37
from bzrlib.transport import Transport, urlescape
38
38
from bzrlib.transport.local import LocalTransport
40
40
######################################################################
238
238
raise KeyError(fileid)
240
240
def __init__(self, a_transport, prefixed=False, compressed=False,
241
dir_mode=None, file_mode=None):
242
assert isinstance(a_transport, transport.Transport)
241
dir_mode=None, file_mode=None,
243
assert isinstance(a_transport, Transport)
243
244
super(TransportStore, self).__init__()
244
245
self._transport = a_transport
245
246
self._prefixed = prefixed
246
247
# FIXME RBC 20051128 this belongs in TextStore.
247
248
self._compressed = compressed
248
249
self._suffixes = set()
250
self._escaped = escaped
250
252
# It is okay for these to be None, it just means they
251
253
# will just use the filesystem defaults
252
254
self._dir_mode = dir_mode
253
255
self._file_mode = file_mode
257
def _unescape(self, file_id):
258
"""If filename escaping is enabled for this store, unescape and return the filename."""
260
return urllib.unquote(file_id)
255
264
def _iter_files_recursive(self):
256
265
"""Iterate through the files in the transport."""
257
266
for quoted_relpath in self._transport.iter_files_recursive():
267
# transport iterator always returns quoted paths, regardless of
258
269
yield urllib.unquote(quoted_relpath)
260
271
def __iter__(self):
286
297
if self._prefixed:
287
path = [hash_prefix(fileid) + fileid]
298
# hash_prefix adds the '/' separator
299
prefix = self.hash_prefix(fileid)
290
path.extend(suffixes)
291
return transport.urlescape(u'.'.join(path))
302
fileid = self._escape_file_id(fileid)
303
path = prefix + fileid
304
full_path = u'.'.join([path] + suffixes)
305
return urlescape(full_path)
307
def _escape_file_id(self, file_id):
308
"""Turn a file id into a filesystem safe string.
310
This is similar to a plain urllib.quote, except
311
it uses specific safe characters, so that it doesn't
312
have to translate a lot of valid file ids.
314
if not self._escaped:
316
if isinstance(file_id, unicode):
317
file_id = file_id.encode('utf-8')
318
# @ does not get escaped. This is because it is a valid
319
# filesystem character we use all the time, and it looks
320
# a lot better than seeing %40 all the time.
321
safe = "abcdefghijklmnopqrstuvwxyz0123456789-_@,."
322
r = [((c in safe) and c or ('%%%02x' % ord(c)))
326
def hash_prefix(self, fileid):
327
# fileid should be unescaped
329
fileid = self._escape_file_id(fileid)
330
return "%02x/" % (adler32(fileid) & 0xff)
293
332
def __repr__(self):
294
333
if self._transport is None:
323
362
return count, total
326
def ImmutableMemoryStore():
327
return bzrlib.store.text.TextStore(transport.memory.MemoryTransport())
330
365
@deprecated_function(zero_eight)
331
366
def copy_all(store_from, store_to, pb=None):
332
367
"""Copy all ids from one store to another."""
333
368
store_to.copy_all_ids(store_from, pb)
336
def hash_prefix(fileid):
337
return "%02x/" % (adler32(fileid) & 0xff)