// $Id: module.pmod.in,v 1.17 2008/06/28 17:07:19 nilsson Exp $ |
#pike __REAL_VERSION__ |
|
inherit @module@; |
|
#if constant(@module@.inflate) |
|
//! Low-level implementation of read/write support for GZip files |
class _file { |
|
protected private Stdio.Stream f; |
protected private inflate inf; |
protected private deflate def; |
protected private int level, strategy; |
protected private string read_buf; |
protected private int file_pos, crc, write_mode, at_eof; |
|
constant SEEK_SET = 0; |
constant SEEK_CUR = 1; |
constant SEEK_END = 2; |
|
protected int check_header(string|void buf) |
{ |
int magic1, magic2, method, flags, len; |
|
if(!buf) buf=""; |
string buf_read(int n) |
{ |
if(sizeof(buf)<n) |
buf += f->read(min(n-sizeof(buf), 10)); |
string r = buf[..n-1]; |
buf = buf[n..]; |
return r; |
}; |
|
if(sscanf(buf_read(4), "%1c%1c%1c%1c", magic1, magic2, method, flags)!=4 || |
magic1 != 0x1f || magic2 != 0x8b) |
return 0; |
|
if(method != 8 || (flags & 0xe0)) |
return 0; |
|
if(sizeof(buf_read(6)) != 6) |
return 0; |
|
if(flags & 4) |
if(sscanf(buf_read(2), "%-2c", len) != 1 || |
sizeof(buf_read(len)) != len) |
return 0; |
|
if(flags & 8) |
loop: for(;;) |
switch(buf_read(1)) { |
case "": return 0; |
case "\0": break loop; |
} |
|
if(flags & 16) |
loop: for(;;) |
switch(buf_read(1)) { |
case "": return 0; |
case "\0": break loop; |
} |
|
if(flags & 2) |
if(sizeof(buf_read(2)) != 2) |
return 0; |
|
if(sizeof(buf)) |
fill_read_buffer(buf); |
|
return 1; |
} |
|
protected int make_header() |
{ |
return f->write(sprintf("%1c%1c%1c%1c%4c%1c%1c", |
0x1f, 0x8b, 8, 0, 0, 0, 3)) == 10; |
} |
|
//! Opens a file for I/O. |
//! |
//! @param file |
//! The filename or an open filedescriptor or Stream for the GZip |
//! file to use. |
//! @param mode |
//! Mode for the fileoperations. Defaults to read only. |
//! |
//! @note |
//! If the object already has been opened, it will first be closed. |
int open(string|int|Stdio.Stream file, void|string mode) |
{ |
close(); |
write_mode = 0; |
level = 6; |
strategy = DEFAULT_STRATEGY; |
if(mode) { |
mode = filter(mode, lambda(int n) { |
if(n == 'w' || n == 'a') |
write_mode = 1; |
if(n >= '0' && n <= '9') |
level = n - '0'; |
else if(n == 'f') |
strategy = FILTERED; |
else if(n == 'h') |
strategy = HUFFMAN_ONLY; |
else |
return 1; |
}); |
if(write_mode) |
mode += "c"+(has_value(mode, 'a')? "w":"t"); |
} |
at_eof = file_pos = 0; |
crc = crc32(""); |
if(objectp(file)) |
f = file; |
else { |
f = Stdio.File(); |
if(!f->open(file, mode||"rb")) |
return 0; |
} |
return write_mode? make_header() : check_header(); |
} |
|
//! Opens a gzip file for reading. |
protected void create(void|string|Stdio.Stream gzFile, void|string mode) |
{ |
if(!zero_type(gzFile) && !open(gzFile, mode)) |
error("Failed to open file.\n"); |
} |
|
//! closes the file |
//! @returns |
//! 1 if successful |
int close() |
{ |
if(def) { |
string s = def->deflate("", FINISH); |
if(sizeof(s) && f->write(s) != sizeof(s)) |
return 0; |
if(f->write(sprintf("%-4c%-4c", crc, file_pos)) != 8) |
return 0; |
} |
inf = 0; |
def = 0; |
read_buf = ""; |
Stdio.File oldf = f; |
f = 0; |
return !oldf || oldf->close(); |
} |
|
protected int fill_read_buffer(string|void data) |
{ |
if(at_eof) |
return 0; |
string r = data || f->read(16384); |
if(!sizeof(r)) { |
at_eof = 1; |
return 0; |
} |
if(!inf) inf = inflate(-15); |
string b = inf->inflate(r); |
read_buf += b; |
crc = crc32(b, crc); |
if(b = inf->end_of_stream()) { |
inf = 0; |
if(sizeof(b)<8) |
b += f->read(8-sizeof(b)); |
sscanf(b, "%-4c%-4c", int f_crc, int f_len); |
#ifdef GZ_FILE_DEBUG |
werror("File: crc=%x size=%d Internal: crc=%x size=%d\n", |
f_crc, f_len, crc&0xffffffff, file_pos+sizeof(read_buf)); |
#endif |
if(f_crc != (crc&0xffffffff)) { |
// CRC error |
at_eof = 1; |
return 0; |
} else { |
crc = crc32(""); |
if(!check_header(b[8..])) |
at_eof = 1; |
} |
} |
return sizeof(r); |
} |
|
//! Reads len (uncompressed) bytes from the file. |
//! If read is unsuccessful, 0 is returned. |
int|string read(int len) |
{ |
while(sizeof(read_buf) < len) |
if(!fill_read_buffer()) |
break; |
string res = read_buf[..len-1]; |
read_buf = read_buf[len..]; |
file_pos += sizeof(res); |
return res; |
} |
|
//! Writes the data to the file. |
//! @returns |
//! the number of bytes written to the file. |
int write(string data) |
{ |
if(!def) def = deflate(-level, strategy); |
string comp = def->deflate(data, NO_FLUSH); |
if(f->write(comp) != sizeof(comp)) |
return 0; |
else { |
file_pos += sizeof(data); |
crc = crc32(data, crc); |
return sizeof(data); |
} |
} |
|
//! Seeks within the file. |
//! @param pos |
//! Position relative to the searchtype. |
//! @param type |
//! SEEK_SET = set current position in file to pos |
//! SEEK_CUR = new position is current+pos |
//! SEEK_END is not supported. |
//! @returns |
//! New position or negative number if seek failed. |
int seek(int pos, void|int type) |
{ |
if(type != SEEK_SET && type != SEEK_CUR) |
return -1; |
if(write_mode) { |
if(type == SEEK_SET) |
pos -= file_pos; |
if(pos < 0) |
return -1; |
while(pos > 0) { |
int n = write("\0"*(pos>16384? 16384:pos)); |
if(!n) |
return -1; |
pos -= n; |
} |
return file_pos; |
} else { |
if(type == SEEK_CUR) |
pos += file_pos; |
if(pos < 0) |
return -1; |
if(pos < file_pos) { |
if(!f->seek || f->seek(0)<0) |
return -1; |
at_eof = 0; |
file_pos = 0; |
read_buf = ""; |
crc = crc32(""); |
if(!check_header()) |
return -1; |
} else |
pos -= file_pos; |
while(pos > 0) { |
string r = read(pos>16384? 16384:pos); |
if(!sizeof(r)) |
return -1; |
pos -= sizeof(r); |
} |
return file_pos; |
} |
} |
|
//! @returns |
//! the current position within the file. |
int tell() |
{ |
return file_pos; |
} |
|
//! @returns |
//! 1 if EOF has been reached. |
int(0..1) eof() |
{ |
if(at_eof) return 1; |
if(def || write_mode || sizeof(read_buf)) return 0; |
while(!sizeof(read_buf) && fill_read_buffer()) |
; |
return at_eof; |
} |
|
//! Sets the encoding level and strategy |
//! @param level |
//! Level of the compression. |
//! 0 is the least compression, 9 is max. 8 is default. |
//! @param strategy |
//! Set strategy for encoding to one of the following: |
//! DEFAULT_STRATEGY |
//! FILTERED |
//! HUFFMAN_ONLY |
int setparams(int level, int strategy) |
{ |
if(def) { |
string s = def->deflate("", SYNC_FLUSH); |
if(sizeof(s) && f->write(s) != sizeof(s)) |
return 0; |
def = 0; |
} |
_file::level = level; |
_file::strategy = strategy; |
return 1; |
} |
|
} |
|
|
//! Allows the user to open a Gzip archive and read and write |
//! it's contents in an uncompressed form, emulating the @[Stdio.File] |
//! interface. |
//! @note |
//! An important limitation on this class is that it may only be used |
//! for reading @b{or@} writing, not both at the same time. |
//! Please also note that if you want to reopen a file for reading |
//! after a write, you must close the file before calling open or |
//! strange effects might be the result. |
class File { |
inherit _file; |
|
private int is_open = 0; |
|
//! @decl void create(void|string|int|Stdio.Stream file, void|string mode) |
//! @param file |
//! Filename or filedescriptor of the gzip file to open, or an already |
//! open Stream. |
//! @param mode |
//! mode for the file. Defaults to "rb". |
//! @seealso |
//! @[open] @[Stdio.File] |
protected void create(mixed ... args) { |
::create(@args); |
|
if(sizeof(args)) { |
is_open = 1; |
} |
} |
|
protected string _sprintf(int t) |
{ |
switch(t) { |
case 'O': |
return sprintf("Gz.File(/*%s open */)", is_open ? "" : " not"); |
case 't': |
return "Gz.File"; |
default: |
return UNDEFINED; |
} |
} |
|
int close() |
{ |
is_open = 0; |
return ::close(); |
} |
|
protected void destroy() { |
close(); |
} |
|
//! @param file |
//! Filename or filedescriptor of the gzip file to open, or an already |
//! open Stream. |
//! @param mode |
//! mode for the file. Defaults to "rb". |
//! May be one of the following: |
//! @dl |
//! @item rb |
//! read mode |
//! @item wb |
//! write mode |
//! @item ab |
//! append mode |
//! @enddl |
//! For the wb and ab mode, additional parameters may |
//! be specified. Please se zlib manual for more info. |
//! @returns |
//! non-zero if successful. |
int open(string|int|Stdio.Stream file, void|string mode) { |
string open_mode="rb"; |
if (is_open) { |
::close(); |
} |
|
if (stringp(mode)) { |
open_mode = lower_case(mode); |
} |
|
is_open = ::open(file, open_mode); |
return is_open; |
} |
|
//! Reads data from the file. |
//! If no argument is given, the whole file is read. |
int|string read(void|int length) { |
if (!is_open) { |
return 0; |
} |
|
if (!zero_type(length)) |
return ::read(length); |
|
String.Buffer buf = String.Buffer(); |
string data; |
do { |
if (!(data = ::read(1024*64))) break; |
buf->add(data); |
} while (sizeof(data)); |
return (string)buf; |
} |
} |
|
#endif // constant(@module@.inflate) |
|