35f51c2008-06-28Martin Nilsson // $Id: module.pmod.in,v 1.17 2008/06/28 17:07:19 nilsson Exp $
94dd762002-11-24Marcus Agehall #pike __REAL_VERSION__ inherit @module@;
2a72282003-04-23Martin Nilsson #if constant(@module@.inflate)
b8f7652003-04-14Marcus Comstedt //! Low-level implementation of read/write support for GZip files class _file {
35f51c2008-06-28Martin Nilsson  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;
b8f7652003-04-14Marcus Comstedt  constant SEEK_SET = 0; constant SEEK_CUR = 1; constant SEEK_END = 2;
35f51c2008-06-28Martin Nilsson  protected int check_header(string|void buf)
b8f7652003-04-14Marcus Comstedt  { int magic1, magic2, method, flags, len;
38eba32003-04-15Marcus Comstedt  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 ||
b8f7652003-04-14Marcus Comstedt  magic1 != 0x1f || magic2 != 0x8b) return 0; if(method != 8 || (flags & 0xe0)) return 0;
38eba32003-04-15Marcus Comstedt  if(sizeof(buf_read(6)) != 6)
b8f7652003-04-14Marcus Comstedt  return 0; if(flags & 4)
38eba32003-04-15Marcus Comstedt  if(sscanf(buf_read(2), "%-2c", len) != 1 || sizeof(buf_read(len)) != len)
b8f7652003-04-14Marcus Comstedt  return 0; if(flags & 8) loop: for(;;)
38eba32003-04-15Marcus Comstedt  switch(buf_read(1)) { case "": return 0;
b8f7652003-04-14Marcus Comstedt  case "\0": break loop; } if(flags & 16) loop: for(;;)
38eba32003-04-15Marcus Comstedt  switch(buf_read(1)) { case "": return 0;
b8f7652003-04-14Marcus Comstedt  case "\0": break loop; } if(flags & 2)
38eba32003-04-15Marcus Comstedt  if(sizeof(buf_read(2)) != 2)
b8f7652003-04-14Marcus Comstedt  return 0;
38eba32003-04-15Marcus Comstedt  if(sizeof(buf)) fill_read_buffer(buf);
b8f7652003-04-14Marcus Comstedt  return 1; }
35f51c2008-06-28Martin Nilsson  protected int make_header()
b8f7652003-04-14Marcus Comstedt  { 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; });
28078d2003-04-15Marcus Comstedt  if(write_mode) mode += "c"+(has_value(mode, 'a')? "w":"t");
b8f7652003-04-14Marcus Comstedt  }
38eba32003-04-15Marcus Comstedt  at_eof = file_pos = 0;
425d502003-04-15Marcus Comstedt  crc = crc32("");
b8f7652003-04-14Marcus Comstedt  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.
35f51c2008-06-28Martin Nilsson  protected void create(void|string|Stdio.Stream gzFile, void|string mode)
b8f7652003-04-14Marcus Comstedt  { 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(); }
35f51c2008-06-28Martin Nilsson  protected int fill_read_buffer(string|void data)
38eba32003-04-15Marcus Comstedt  { 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); }
b8f7652003-04-14Marcus Comstedt  //! Reads len (uncompressed) bytes from the file. //! If read is unsuccessful, 0 is returned. int|string read(int len) {
38eba32003-04-15Marcus Comstedt  while(sizeof(read_buf) < len) if(!fill_read_buffer())
b8f7652003-04-14Marcus Comstedt  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) {
425d502003-04-15Marcus Comstedt  if(!def) def = deflate(-level, strategy);
b8f7652003-04-14Marcus Comstedt  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;
6794d52003-04-15Marcus Comstedt  if(pos < file_pos) { if(!f->seek || f->seek(0)<0) return -1;
38eba32003-04-15Marcus Comstedt  at_eof = 0;
6794d52003-04-15Marcus Comstedt  file_pos = 0; read_buf = ""; crc = crc32("");
b132a02003-04-15Marcus Comstedt  if(!check_header()) return -1;
6794d52003-04-15Marcus Comstedt  } else pos -= file_pos;
b8f7652003-04-14Marcus Comstedt  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() {
38eba32003-04-15Marcus Comstedt  if(at_eof) return 1; if(def || write_mode || sizeof(read_buf)) return 0; while(!sizeof(read_buf) && fill_read_buffer()) ; return at_eof;
b8f7652003-04-14Marcus Comstedt  } //! 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) {
425d502003-04-15Marcus Comstedt  string s = def->deflate("", SYNC_FLUSH);
b8f7652003-04-14Marcus Comstedt  if(sizeof(s) && f->write(s) != sizeof(s)) return 0; def = 0; } _file::level = level; _file::strategy = strategy; return 1; } }
94dd762002-11-24Marcus Agehall  //! 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 {
2be96e2002-11-25Martin Nilsson  inherit _file;
94dd762002-11-24Marcus Agehall 
8f83ac2002-11-24Marcus Agehall  private int is_open = 0;
94dd762002-11-24Marcus Agehall 
b8f7652003-04-14Marcus Comstedt  //! @decl void create(void|string|int|Stdio.Stream file, void|string mode)
94dd762002-11-24Marcus Agehall  //! @param file
b8f7652003-04-14Marcus Comstedt  //! Filename or filedescriptor of the gzip file to open, or an already //! open Stream.
94dd762002-11-24Marcus Agehall  //! @param mode //! mode for the file. Defaults to "rb". //! @seealso //! @[open] @[Stdio.File]
35f51c2008-06-28Martin Nilsson  protected void create(mixed ... args) {
58a5ad2005-04-02Martin Nilsson  ::create(@args);
94dd762002-11-24Marcus Agehall 
8f83ac2002-11-24Marcus Agehall  if(sizeof(args)) {
58a5ad2005-04-02Martin Nilsson  is_open = 1;
8f83ac2002-11-24Marcus Agehall  }
94dd762002-11-24Marcus Agehall  }
35f51c2008-06-28Martin Nilsson  protected string _sprintf(int t)
94dd762002-11-24Marcus Agehall  {
8f83ac2002-11-24Marcus Agehall  switch(t) { case 'O': return sprintf("Gz.File(/*%s open */)", is_open ? "" : " not"); case 't': return "Gz.File"; default:
ea203b2002-11-25Henrik Grubbström (Grubba)  return UNDEFINED;
8f83ac2002-11-24Marcus Agehall  }
94dd762002-11-24Marcus Agehall  } int close() { is_open = 0; return ::close(); }
35f51c2008-06-28Martin Nilsson  protected void destroy() {
94dd762002-11-24Marcus Agehall  close(); } //! @param file
b8f7652003-04-14Marcus Comstedt  //! Filename or filedescriptor of the gzip file to open, or an already //! open Stream.
94dd762002-11-24Marcus Agehall  //! @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.
b8f7652003-04-14Marcus Comstedt  int open(string|int|Stdio.Stream file, void|string mode) {
94dd762002-11-24Marcus Agehall  string open_mode="rb";
8f83ac2002-11-24Marcus Agehall  if (is_open) {
94dd762002-11-24Marcus Agehall  ::close();
8f83ac2002-11-24Marcus Agehall  }
94dd762002-11-24Marcus Agehall  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) {
8f83ac2002-11-24Marcus Agehall  if (!is_open) {
94dd762002-11-24Marcus Agehall  return 0;
8f83ac2002-11-24Marcus Agehall  }
94dd762002-11-24Marcus Agehall  if (!zero_type(length)) return ::read(length);
b568272002-11-25Henrik Grubbström (Grubba)  String.Buffer buf = String.Buffer();
d20c702002-11-25Henrik Grubbström (Grubba)  string data;
b568272002-11-25Henrik Grubbström (Grubba)  do { if (!(data = ::read(1024*64))) break; buf->add(data); } while (sizeof(data)); return (string)buf;
94dd762002-11-24Marcus Agehall  } }
0d62402002-11-25Henrik Grubbström (Grubba) 
2a72282003-04-23Martin Nilsson #endif // constant(@module@.inflate)