6794d52003-04-15Marcus Comstedt // $Id: module.pmod.in,v 1.11 2003/04/15 13:42:34 marcus Exp $
94dd762002-11-24Marcus Agehall #pike __REAL_VERSION__ inherit @module@;
b8f7652003-04-14Marcus Comstedt //! Low-level implementation of read/write support for GZip files class _file { static private Stdio.Stream f; static private inflate inf; static private deflate def; static private int level, strategy; static private string read_buf; static private int file_pos, crc, write_mode; constant SEEK_SET = 0; constant SEEK_CUR = 1; constant SEEK_END = 2; static int check_header() { int magic1, magic2, method, flags, len; if(sscanf(f->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(f->read(6)) != 6) return 0; if(flags & 4) if(sscanf(f->read(2), "%-2c", len) != 1 || sizeof(f->read(len)) != len) return 0; if(flags & 8) loop: for(;;) switch(f->read(1)) { case 0: case "": return 0; case "\0": break loop; } if(flags & 16) loop: for(;;) switch(f->read(1)) { case 0: case "": return 0; case "\0": break loop; } if(flags & 2) if(sizeof(f->read(2)) != 2) return 0; return 1; } static 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) { if(!has_value(mode, 'a')) mode += "t"; mode += "c"; } } 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. static 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(); } //! Reads len (uncompressed) bytes from the file. //! If read is unsuccessful, 0 is returned. int|string read(int len) { if(!inf) inf = inflate(-15); while(sizeof(read_buf) < len) { string r = f->read(16384); if(!sizeof(r)) break; read_buf += inf->inflate(r); } string res = read_buf[..len-1]; read_buf = read_buf[len..]; file_pos += sizeof(res);
425d502003-04-15Marcus Comstedt  crc = crc32(res, crc);
b8f7652003-04-14Marcus Comstedt  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; file_pos = 0; read_buf = ""; crc = crc32(""); } 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() { if(def || sizeof(read_buf)) return 0; if(!inf) inf = inflate(-15); string r = f->read(16384); if(!sizeof(r)) return 1; read_buf = inf->inflate(r); return !sizeof(read_buf); } //! 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]
ea203b2002-11-25Henrik Grubbström (Grubba)  static void create(mixed ... args) {
94dd762002-11-24Marcus Agehall  ::create();
8f83ac2002-11-24Marcus Agehall  if(sizeof(args)) {
94dd762002-11-24Marcus Agehall  open(@args);
8f83ac2002-11-24Marcus Agehall  }
94dd762002-11-24Marcus Agehall  }
ea203b2002-11-25Henrik Grubbström (Grubba)  static 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(); }
ea203b2002-11-25Henrik Grubbström (Grubba)  static 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)