#!/usr/bin/env python
"""Calculating checksums with different algorithms in one pass.
"""

__author__ = "Marc 'BlackJack' Rintsch"
__version__ = '0.0.1a'
__date__ = '$Date: 2005-02-27 12:45:29 +0100 (Sun, 27 Feb 2005) $'
__revision__ = '$Rev: 641 $'

__docformat__ = 'reStructuredText'

from __future__ import division
import sys
import md5, sha


def block_reader(infile, blocksize=8192):
    """Iterates over a file in blocks of bytes.
    
    Returns an iterator which reads `blocksize` bytes at each call to
    its next() method.  Except for the last block where less bytes
    may be returned.
    
    :param infile: file to read.
    :type infile: file
    :param blocksize: size of each block in bytes.
    :type blocksize: int
    
    :returns: iterable of data blocks.
    :rtype: iterable of `str`
    """
    while True:
        block = infile.read(blocksize)
        if not block:
            break
        else:
            yield block


class Checksums:
    """Checksums for one file.
    
    :ivar path: name of the file prepended by a '*' if the file was
        opened in binary mode or a ' ' if it was opened in text mode.
    :type path: str
    :ivar checksums: checksums as hexdigests.
    :type checksums: [str]
    """
    def __init__(self, summers, path, binary=False):
        """Creates `Checksums` object and calculates the checksums.
        
        :param summers: objects that implement the checksummer protocol
            (new(), update() and hexdigest()).
        :param path: name of the file to calculate the checksum of.
        :type path: str
        :param binary: open the file in binary mode or not.
        :type binary: bool
        
        :raises IOError: if the file can't be opened or read.
        """
        summers = [summer.new() for summer in summers]
        
        if binary:
            self.path = '*' + path
            mode = 'rb'
        else:
            self.path = ' ' + path
            mode = 'r'
        
        if path == '-':
            infile = sys.stdin
        else:
            infile = open(path, mode)

        try:
            for block in block_reader(infile):
                for summer in summers:
                    summer.update(block)
        finally:
            infile.close()
        
        self.checksums = [summer.hexdigest() for summer in summers]
    
    def __str__(self):
        return '\n'.join(['%s %s' % (checksum, self.path)
                          for checksum in self.checksums])


def main():
    """Main function."""
    
    filenames = sys.argv[1:]
    
    if not filenames:
        filenames = ['-']
    
    for filename in filenames:
        try:
            print Checksums((md5, sha), filename)
        except IOError, error:
            print >> sys.stderr, error


if __name__ == '__main__':
    main()
