|
1 | 1 | from __future__ import division, print_function, unicode_literals |
2 | 2 |
|
3 | 3 | from contextlib import contextmanager |
4 | | -from ctypes import cast, c_void_p |
| 4 | +from ctypes import cast, c_void_p, POINTER, create_string_buffer |
5 | 5 | from os import fstat, stat |
6 | 6 |
|
7 | 7 | from . import ffi |
8 | | -from .ffi import ARCHIVE_EOF |
| 8 | +from .ffi import (ARCHIVE_EOF, OPEN_CALLBACK, READ_CALLBACK, CLOSE_CALLBACK, |
| 9 | + VOID_CB, page_size) |
9 | 10 | from .entry import ArchiveEntry, new_archive_entry |
10 | 11 |
|
11 | 12 |
|
@@ -43,6 +44,22 @@ def new_archive_read(format_name='all', filter_name='all'): |
43 | 44 | ffi.read_free(archive_p) |
44 | 45 |
|
45 | 46 |
|
| 47 | +@contextmanager |
| 48 | +def custom_reader( |
| 49 | + read_func, format_name='all', filter_name='all', |
| 50 | + open_func=VOID_CB, close_func=VOID_CB, block_size=page_size, |
| 51 | + archive_read_class=ArchiveRead |
| 52 | +): |
| 53 | + """Read an archive using a custom function. |
| 54 | + """ |
| 55 | + open_cb = OPEN_CALLBACK(open_func) |
| 56 | + read_cb = READ_CALLBACK(read_func) |
| 57 | + close_cb = CLOSE_CALLBACK(close_func) |
| 58 | + with new_archive_read(format_name, filter_name) as archive_p: |
| 59 | + ffi.read_open(archive_p, None, open_cb, read_cb, close_cb) |
| 60 | + yield archive_read_class(archive_p) |
| 61 | + |
| 62 | + |
46 | 63 | @contextmanager |
47 | 64 | def fd_reader(fd, format_name='all', filter_name='all', block_size=4096): |
48 | 65 | """Read an archive from a file descriptor. |
@@ -76,3 +93,27 @@ def memory_reader(buf, format_name='all', filter_name='all'): |
76 | 93 | with new_archive_read(format_name, filter_name) as archive_p: |
77 | 94 | ffi.read_open_memory(archive_p, cast(buf, c_void_p), len(buf)) |
78 | 95 | yield ArchiveRead(archive_p) |
| 96 | + |
| 97 | + |
| 98 | +@contextmanager |
| 99 | +def stream_reader(stream, format_name='all', filter_name='all', block_size=page_size): |
| 100 | + """Read an archive from a stream (an object supporting the `readinto` method). |
| 101 | + """ |
| 102 | + buf = create_string_buffer(block_size) |
| 103 | + buf_p = cast(buf, c_void_p) |
| 104 | + |
| 105 | + def read_func(archive_p, context, ptrptr): |
| 106 | + # readinto the buffer, returns number of bytes read |
| 107 | + length = stream.readinto(buf) |
| 108 | + # write the address of the buffer into the pointer |
| 109 | + ptrptr = cast(ptrptr, POINTER(c_void_p)) |
| 110 | + ptrptr[0] = buf_p |
| 111 | + # tell libarchive how much data was written into the buffer |
| 112 | + return length |
| 113 | + |
| 114 | + open_cb = OPEN_CALLBACK(VOID_CB) |
| 115 | + read_cb = READ_CALLBACK(read_func) |
| 116 | + close_cb = CLOSE_CALLBACK(VOID_CB) |
| 117 | + with new_archive_read(format_name, filter_name) as archive_p: |
| 118 | + ffi.read_open(archive_p, None, open_cb, read_cb, close_cb) |
| 119 | + yield ArchiveRead(archive_p) |
0 commit comments