pike.git / src / modules / Gz / module.pmod.in

version» Context lines:

pike.git/src/modules/Gz/module.pmod.in:1: + // -*- pike -*- + #pike __REAL_VERSION__    -  + #if constant(@module@) +  + inherit @module@; +  + //! 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; +  +  final constant SEEK_SET = 0; +  final constant SEEK_CUR = 1; +  final 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; +  inf = 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; +  } + } +  + #else + constant this_program_does_not_exist = 1; + #endif // constant(@module@)   Newline at end of file added.