aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'cvs2svn_lib/apple_single_filter.py')
-rw-r--r--cvs2svn_lib/apple_single_filter.py292
1 files changed, 0 insertions, 292 deletions
diff --git a/cvs2svn_lib/apple_single_filter.py b/cvs2svn_lib/apple_single_filter.py
deleted file mode 100644
index 95fa9cb..0000000
--- a/cvs2svn_lib/apple_single_filter.py
+++ /dev/null
@@ -1,292 +0,0 @@
-# (Be in -*- python -*- mode.)
-#
-# ====================================================================
-# Copyright (c) 2007-2008 CollabNet. All rights reserved.
-#
-# This software is licensed as described in the file COPYING, which
-# you should have received as part of this distribution. The terms
-# are also available at http://subversion.tigris.org/license-1.html.
-# If newer versions of this license are posted there, you may use a
-# newer version instead, at your option.
-#
-# This software consists of voluntary contributions made by many
-# individuals. For exact contribution history, see the revision
-# history and logs, available at http://cvs2svn.tigris.org/.
-# ====================================================================
-
-"""A stream filter for extracting the data fork from AppleSingle data.
-
-Some Macintosh CVS clients store resource fork data along with the
-contents of the file (called the data fork) by encoding both in an
-'AppleSingle' data stream before storing them to CVS. This file
-contains a stream filter for extracting the data fork from such data
-streams. (Any other forks are discarded.)
-
-See the following for some random information about this format and
-how it is used by Macintosh CVS clients:
-
- http://users.phg-online.de/tk/netatalk/doc/Apple/v1/
- http://rfc.net/rfc1740.html
- http://ximbiot.com/cvs/cvshome/cyclic/cvs/dev-mac.html
- http://www.maccvs.org/faq.html#resfiles
- http://www.heilancoo.net/MacCVSClient/MacCVSClientDoc/storage-formats.html
-
-"""
-
-
-import struct
-from cStringIO import StringIO
-
-
-class AppleSingleFormatError(IOError):
- """The stream was not in correct AppleSingle format."""
-
- pass
-
-
-class AppleSingleIncorrectMagicError(AppleSingleFormatError):
- """The file didn't start with the correct magic number."""
-
- def __init__(self, data_read, eof):
- AppleSingleFormatError.__init__(self)
- self.data_read = data_read
- self.eof = eof
-
-
-class AppleSingleEOFError(AppleSingleFormatError):
- """EOF was reached where AppleSingle doesn't allow it."""
-
- pass
-
-
-class AppleSingleFilter(object):
- """A stream that reads the data fork from an AppleSingle stream.
-
- If the constructor discovers that the file is not a legitimate
- AppleSingle stream, then it raises an AppleSingleFormatError. In
- the special case that the magic number is incorrect, it raises
- AppleSingleIncorrectMagicError with data_read set to the data that
- have been read so far from the input stream. (This allows the
- caller the option to fallback to treating the input stream as a
- normal binary data stream.)"""
-
- # The header is:
- #
- # Magic number 4 bytes
- # Version number 4 bytes
- # File system or filler 16 bytes
- # Number of entries 2 bytes
- magic_struct = '>i'
- magic_len = struct.calcsize(magic_struct)
-
- # The part of the header after the magic number:
- rest_of_header_struct = '>i16sH'
- rest_of_header_len = struct.calcsize(rest_of_header_struct)
-
- # Each entry is:
- #
- # Entry ID 4 bytes
- # Offset 4 bytes
- # Length 4 bytes
- entry_struct = '>iii'
- entry_len = struct.calcsize(entry_struct)
-
- apple_single_magic = 0x00051600
- apple_single_version_1 = 0x00010000
- apple_single_version_2 = 0x00020000
- apple_single_filler = '\0' * 16
-
- apple_single_data_fork_entry_id = 1
-
- def __init__(self, stream):
- self.stream = stream
-
- # Check for the AppleSingle magic number:
- s = self._read_exactly(self.magic_len)
- if len(s) < self.magic_len:
- raise AppleSingleIncorrectMagicError(s, True)
-
- (magic,) = struct.unpack(self.magic_struct, s)
- if magic != self.apple_single_magic:
- raise AppleSingleIncorrectMagicError(s, False)
-
- # Read the rest of the header:
- s = self._read_exactly(self.rest_of_header_len)
- if len(s) < self.rest_of_header_len:
- raise AppleSingleEOFError('AppleSingle header incomplete')
-
- (version, filler, num_entries) = \
- struct.unpack(self.rest_of_header_struct, s)
-
- if version == self.apple_single_version_1:
- self._prepare_apple_single_v1_file(num_entries)
- elif version == self.apple_single_version_2:
- if filler != self.apple_single_filler:
- raise AppleSingleFormatError('Incorrect filler')
- self._prepare_apple_single_v2_file(num_entries)
- else:
- raise AppleSingleFormatError('Unknown AppleSingle version')
-
- def _read_exactly(self, size):
- """Read and return exactly SIZE characters from the stream.
-
- This method is to deal with the fact that stream.read(size) is
- allowed to return less than size characters. If EOF is reached
- before SIZE characters have been read, return the characters that
- have been read so far."""
-
- retval = []
- length_remaining = size
- while length_remaining > 0:
- s = self.stream.read(length_remaining)
- if not s:
- break
- retval.append(s)
- length_remaining -= len(s)
-
- return ''.join(retval)
-
- def _prepare_apple_single_file(self, num_entries):
- entries = self._read_exactly(num_entries * self.entry_len)
- if len(entries) < num_entries * self.entry_len:
- raise AppleSingleEOFError('Incomplete entries list')
-
- for i in range(num_entries):
- entry = entries[i * self.entry_len : (i + 1) * self.entry_len]
- (entry_id, offset, length) = struct.unpack(self.entry_struct, entry)
- if entry_id == self.apple_single_data_fork_entry_id:
- break
- else:
- raise AppleSingleFormatError('No data fork found')
-
- # The data fork is located at [offset : offset + length]. Read up
- # to the start of the data:
- n = offset - self.magic_len - self.rest_of_header_len - len(entries)
- if n < 0:
- raise AppleSingleFormatError('Invalid offset to AppleSingle data fork')
-
- max_chunk_size = 65536
- while n > 0:
- s = self.stream.read(min(n, max_chunk_size))
- if not s:
- raise AppleSingleEOFError(
- 'Offset to AppleSingle data fork past end of file'
- )
- n -= len(s)
-
- self.length_remaining = length
-
- def _prepare_apple_single_v1_file(self, num_entries):
- self._prepare_apple_single_file(num_entries)
-
- def _prepare_apple_single_v2_file(self, num_entries):
- self._prepare_apple_single_file(num_entries)
-
- def read(self, size=-1):
- if size == 0 or self.length_remaining == 0:
- return ''
- elif size < 0:
- s = self._read_exactly(self.length_remaining)
- if len(s) < self.length_remaining:
- raise AppleSingleEOFError('AppleSingle data fork truncated')
- self.length_remaining = 0
- return s
- else:
- # The length of this read is allowed to be shorter than the
- # requested size:
- s = self.stream.read(min(size, self.length_remaining))
- if not s:
- raise AppleSingleEOFError()
- self.length_remaining -= len(s)
- return s
-
- def close(self):
- self.stream.close()
- self.stream = None
-
-
-class CompoundStream(object):
- """A stream that reads from a series of streams, one after the other."""
-
- def __init__(self, *streams):
- self.streams = list(streams)
- self.stream_index = 0
-
- def read(self, size=-1):
- if size < 0:
- retval = []
- while self.stream_index < len(self.streams):
- retval.append(self.streams[self.stream_index].read())
- self.stream_index += 1
- return ''.join(retval)
- else:
- while self.stream_index < len(self.streams):
- s = self.streams[self.stream_index].read(size)
- if s:
- # This may not be the full size requested, but that is OK:
- return s
- else:
- # That stream was empty; proceed to the next stream:
- self.stream_index += 1
-
- # No streams are left:
- return ''
-
- def close(self):
- for stream in self.streams:
- stream.close()
- self.streams = None
-
-
-def get_maybe_apple_single_stream(stream):
- """Treat STREAM as AppleSingle if possible; otherwise treat it literally.
-
- If STREAM is in AppleSingle format, then return a stream that will
- output the data fork of the original stream. Otherwise, return a
- stream that will output the original file contents literally.
-
- Be careful not to read from STREAM after it has already hit EOF."""
-
- try:
- return AppleSingleFilter(stream)
- except AppleSingleIncorrectMagicError, e:
- # This is OK; the file is not AppleSingle, so we read it normally:
- string_io = StringIO(e.data_read)
- if e.eof:
- # The original stream already reached EOF, so the part already
- # read contains the complete file contents:
- return string_io
- else:
- # The stream needs to output the part already read followed by
- # whatever hasn't been read of the original stream:
- return CompoundStream(string_io, stream)
-
-
-if __name__ == '__main__':
- # For fun and testing, allow use of this file as a pipe if it is
- # invoked as a script. Specifically, if stdin is in AppleSingle
- # format, then output only its data fork; otherwise, output it
- # unchanged.
- #
- # This might not work on systems where sys.stdin is opened in text
- # mode.
- #
- # Remember to set PYTHONPATH to point to the main cvs2svn directory.
-
- import sys
-
- #CHUNK_SIZE = -1
- CHUNK_SIZE = 100
-
- f = get_maybe_apple_single_stream(sys.stdin)
-
- if CHUNK_SIZE < 0:
- sys.stdout.write(f.read())
- else:
- while True:
- s = f.read(CHUNK_SIZE)
- if not s:
- break
- sys.stdout.write(s)
-
-