Viewing the contents of
singlerar.py. Use the following link to view the file
in raw, non-highlighted format:
singlerar.py
#!/usr/bin/env python
""" singlerar.py - checks whether a RAR file is a single archive or the
start of a multi-volume archive.
Usage: singlerar.py <rar file>
The return code is zero if the file is a single archive or the first
volume of a multi-volume archive, or non-zero if it contains at least one
file that is continued from a previous volume.
Author: Per Jonsson
This code is in the public domain. You may use it for whatever you want,
as long as you do not hold the author responsible for the consequences of
its usage.
"""
import sys
__version__ = "0.1"
def rar_is_single_or_start_volume(fname):
"""rar_is_single_or_start_volume(fname) -> boolean/none
Determines if the file specified by fname is a single (standalone) RAR
archive or if it is the first file of a multi-volume archive. It is
considered so if it does not contain any files that are continued from
a previous volume.
Returns one of None, True or False. None is returned if the RAR file
has an invalid signature (and thus isn't a valid archive file). True or
False is returned otherwise, in accordance with the purpose of the
function. Exceptions raised when reading the file must be handled by the
caller.
"""
def data_to_num(data, idx, size):
"""data_to_num(data, idx, size) -> numeric
Converts a sequence of size bytes starting at index idx in the
string data to a long number. The data is assumed to contain the
number in little-endian format.
"""
num = 0L
shift = 0
end = idx + size
while idx < end:
num += long(ord(data[idx])) << shift
shift += 8
idx += 1
return num
def get_data(fd, size):
"""get_data(fd, size) -> string
Reads size bytes from the file object fd (not a system file descriptor).
The data is returned as a string. If not all data could be read (i.e.,
in case of end-of-file), an empty string is returned.
"""
data = fd.read(size)
if len(data) < size:
return ""
else:
return data
def read_rar_block_head(fd):
"""read_rar_block_head(fd) -> tuple
Returns information from the block at the current position in the file
object fd. The return value is a tuple (x, y, z), where x is the
HEAD_CRC field, y is the HEAD_TYPE field and z is the HEAD_FLAGS field.
The block is skipped, so that the current file position is advanced
past the block.
Upon end-of-file, x, y and z in the tuple are all -1.
"""
data = get_data(fd, 7)
if data == "":
return (-1, -1, -1)
head_crc = data_to_num(data, 0, 2)
head_type = data_to_num(data, 2, 1)
head_flags = data_to_num(data, 3, 2)
head_size = data_to_num(data, 5, 2)
if (head_flags & 0x8000) > 0:
data = get_data(fd, 4)
if data == "":
return (-1, -1, -1)
add_size = data_to_num(data, 0, 4)
remain_size = head_size + add_size - 11
else:
remain_size = head_size - 7
if remain_size > 0:
fd.seek(remain_size, 1) # skip data
return (head_crc, head_type, head_flags)
try:
# Open the file for reading in binary mode.
fd = open(fname, "rb")
first_block = True
while 1:
block_head = read_rar_block_head(fd)
if first_block:
# Check the RAR signature.
# HEAD_CRC Always 0x6152
# HEAD_TYPE Header type: 0x72
# HEAD_FLAGS Always 0x1a21
if block_head[0] != 0x6152 or block_head[1] != 0x72 or block_head[2] != 0x1a21:
return None # not a valid RAR archive
first_block = False
if block_head[0] < 0:
# End-of-file
break
if block_head[1] == 0x74:
# File header. If the file is continued from a
# previous volume, then we can return False.
if (block_head[2] & 0x1) > 0:
return False
finally:
fd.close()
# No file was continued from a previous volume, so this is
# either a start volume or a single volume.
return True
if __name__ == "__main__":
if len(sys.argv) < 2:
print "Usage: %s <rar file>" % sys.argv[0]
sys.exit(2)
if rar_is_single_or_start_volume(sys.argv[1]):
sys.exit(0)
sys.exit(1)