~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/store/__init__.py

  • Committer: Martin Pool
  • Date: 2006-04-12 04:45:32 UTC
  • mfrom: (1608.2.13 bzr.mbp.escape-stores)
  • mto: This revision was merged to the branch mainline in revision 1657.
  • Revision ID: mbp@sourcefrog.net-20060412044532-fc8c5c9408aae88b
[merge][wip] Storage escaping

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Canonical Development Ltd
 
1
# Copyright (C) 2005, 2006 by Canonical Development 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
206
206
 
207
207
    def has_id(self, fileid, suffix=None):
208
208
        """See Store.has_id."""
209
 
        return self._transport.has_any(self._id_to_names(fileid, suffix))
 
209
        paths = self._id_to_names(fileid, suffix)
 
210
        if not self._transport.has_any(paths):
 
211
            return False
 
212
        return True
210
213
 
211
214
    def _get_name(self, fileid, suffix=None):
212
215
        """A special check, which returns the name of an existing file.
238
241
        raise KeyError(fileid)
239
242
 
240
243
    def __init__(self, a_transport, prefixed=False, compressed=False,
241
 
                 dir_mode=None, file_mode=None):
 
244
                 dir_mode=None, file_mode=None,
 
245
                 escaped=False):
242
246
        assert isinstance(a_transport, transport.Transport)
243
247
        super(TransportStore, self).__init__()
244
248
        self._transport = a_transport
246
250
        # FIXME RBC 20051128 this belongs in TextStore.
247
251
        self._compressed = compressed
248
252
        self._suffixes = set()
 
253
        self._escaped = escaped
249
254
 
250
255
        # It is okay for these to be None, it just means they
251
256
        # will just use the filesystem defaults
252
257
        self._dir_mode = dir_mode
253
258
        self._file_mode = file_mode
254
259
 
 
260
    def _unescape(self, file_id):
 
261
        """If filename escaping is enabled for this store, unescape and return the filename."""
 
262
        if self._escaped:
 
263
            return urllib.unquote(file_id)
 
264
        else:
 
265
            return file_id
 
266
 
255
267
    def _iter_files_recursive(self):
256
268
        """Iterate through the files in the transport."""
257
269
        for quoted_relpath in self._transport.iter_files_recursive():
 
270
            # transport iterator always returns quoted paths, regardless of
 
271
            # escaping
258
272
            yield urllib.unquote(quoted_relpath)
259
273
 
260
274
    def __iter__(self):
269
283
                    if name.endswith('.' + suffix):
270
284
                        skip = True
271
285
            if not skip:
272
 
                yield name
 
286
                yield self._unescape(name)
273
287
 
274
288
    def __len__(self):
275
289
        return len(list(self.__iter__()))
284
298
        else:
285
299
            suffixes = []
286
300
        if self._prefixed:
287
 
            path = [hash_prefix(fileid) + fileid]
 
301
            # hash_prefix adds the '/' separator
 
302
            prefix = self.hash_prefix(fileid)
288
303
        else:
289
 
            path = [fileid]
290
 
        path.extend(suffixes)
291
 
        return transport.urlescape(u'.'.join(path))
 
304
            prefix = ''
 
305
        fileid = self._escape_file_id(fileid)
 
306
        path = prefix + fileid
 
307
        full_path = u'.'.join([path] + suffixes)
 
308
        return transport.urlescape(full_path)
 
309
 
 
310
    def _escape_file_id(self, file_id):
 
311
        """Turn a file id into a filesystem safe string.
 
312
 
 
313
        This is similar to a plain urllib.quote, except
 
314
        it uses specific safe characters, so that it doesn't
 
315
        have to translate a lot of valid file ids.
 
316
        """
 
317
        if not self._escaped:
 
318
            return file_id
 
319
        if isinstance(file_id, unicode):
 
320
            file_id = file_id.encode('utf-8')
 
321
        # @ does not get escaped. This is because it is a valid
 
322
        # filesystem character we use all the time, and it looks
 
323
        # a lot better than seeing %40 all the time.
 
324
        safe = "abcdefghijklmnopqrstuvwxyz0123456789-_@,."
 
325
        r = [((c in safe) and c or ('%%%02x' % ord(c)))
 
326
             for c in file_id]
 
327
        return ''.join(r)
 
328
 
 
329
    def hash_prefix(self, fileid):
 
330
        # fileid should be unescaped
 
331
        if self._escaped:
 
332
            fileid = self._escape_file_id(fileid)
 
333
        return "%02x/" % (adler32(fileid) & 0xff)
292
334
 
293
335
    def __repr__(self):
294
336
        if self._transport is None:
331
373
def copy_all(store_from, store_to, pb=None):
332
374
    """Copy all ids from one store to another."""
333
375
    store_to.copy_all_ids(store_from, pb)
334
 
 
335
 
 
336
 
def hash_prefix(fileid):
337
 
    return "%02x/" % (adler32(fileid) & 0xff)
338
 
 
339