ZZIP Programmers Interface 
   The complete API description.
  20. July 2002 
 Basics 
  The naming schem of functions in this library follow a simple rule: 
  if you see a function with a zzip_ prefix followed by 
  compact name representing otherwise a C library or posix function then 
  it is a magic wrapper that can automagically handle both real 
  files/directories or zip-contained files. This includes:
| 
  | zzip_opendir | opendir |  | zzip_readdir | readdir |  | zzip_closedir | closedir |  | zzip_rewinddir | rewinddir |  | zzip_telldir | telldir |  | zzip_seekdir | seekdir |  | 
  The ZZIP_DIR handle can wrap both a real directory or a zip-file. 
  Note that you can not open a virtual directory within a
  zip-file, the ZZIP_DIR is either a real DIR-handle of a real 
  directory or the reference of ZIP-file but never a DIR-handle
  within a ZIP-file - there is no such schema of a SUB-DIR handle
  implemented in this library. A ZZIP_DIR does actually represent
  the central directory of a ZIP-file, so that each file entry in 
  this ZZIP-DIR can possibly have a subpath prepended.
  This form of magic has historic reasons as originally the 
  magic wrappers of this library were not meant to wrap a complete
  subtree of a real file tree but only a single directory being
  wrapped with into a zip-file and placed instead. Later proposals
  and patches were coming in to support subtree wrapping by not
  only making a split between the dir-part and file-part but
  going recursivly up through all "/"-dirseparators of a filepath
  given to zzip_open and looking for zip-file there.
  To open a zip-file unconditionally one should be using their
  respective methods that would return a ZZIP_DIR handle being
  the representant memory instance of a ZIP-DIR, the central
  directory of a zip-file. From that ZZIP-DIR one can open a
  compressed file entry which will be returned as a ZZIP_FILE
  pointer.
| 
  | zzip_dir_open | open a zip-file and parse the central directory 
                                              to a memory shadow |  | zzip_dir_close | close a zip-file and free the memory shadow |  | zzip_dir_fdopen | aquire the given posix-file and try to parse it 
                                                  as a zip-file. |  | zzip_dir_read | return the next info entry of a zip-file's central
                 directory - this would include a possible subpath |  | 
  To unconditionally access a zipped-file (as the counter-part of a 
  zip-file's directory) you should be using the functions having a
  zzip_file_ prefix which are the methods working on
  ZZIP_FILE pointers directly and assuming those are references of
  a zipped file with a ZZIP_DIR. 
| 
  | zzip_file_open | open a file within a zip and prepare a zlib 
                     compressor for it - note the ZZIP_DIR argument,
                     multiple ZZIP_FILE's may share the same central
                     directory shadow. |  | zzip_file_close | close the handle of zippedfile
                     and free zlib compressor of it |  | zzip_file_read | decompress the next part of a compressed file
                     within a zip-file |  | 
  From here it is only a short step to the magic wrappers for
  file-access - when being given a filepath to zzip_open then
  the filepath is checked first for being possibly a real file
  (we can often do that by a stat call) and if there is
  a real file under that name then the returned ZZIP_FILE is
  nothing more than a wrapper around a file-descriptor of the
  underlying operating system. Any other calls like zzip_read
  will see the realfd-flag in the ZZIP_FILE and forward the 
  execution to the read() function of the underlying operating system.
  However if that fails then the filepath is cut at last directory
  separator, i.e. a filepath of "this/test/README" is cut into the
  dir-part "this/test" and a file-part "README". Then the possible
  zip-extensions are attached (".zip" and ".ZIP") and we check if
  there is a real file under that name. If a file "this/test.zip"
  does exist then it is given to zzip_dir_open which will create
  a ZZIP_DIR instance of it, and when that was successul (so it
  was in zip-format) then we call zzip_file_open which will see
  two arguments - the just opened ZZIP_DIR and the file-part. The
  resulting ZZIP_FILE has its own copy of a ZZIP_DIR, so if you
  open multiple files from the same zip-file than you will also
  have multiple in-memory copies of the zip's central directory
  whereas otherwise multiple ZZIP_FILE's may share a common
  ZZIP_DIR when being opened with zzip_file_open directly - the
  zzip_file_open's first argument is the ZZIP_DIR and the second
  one the file-part to be looked up within that zip-directory.
| 
  | zzip_open | try the file-path as a real-file, and if not
                     there, look for the existance of ZZIP_DIR by
                     applying extensions, and open the file 
                     contained within that one. |  | zzip_close | if the ZZIP_FILE wraps a real-file, then call
                     read(), otherwise call zzip_file_read() |  | zzip_close | if the ZZIP_FILE wraps a real-file, then call
                     close(), otherwise call zzip_file_close() |  | 
  Up to here we have the original functionality of the zziplib
  when I (Guido Draheim) created the magic functions around the work from 
  Tomi Ollila who wrote the routines to read and decompress files from
  a zip archive - unlike other libraries it was quite readable and
  intelligible source code (after many changes there is not much
  left of the original zip08x source code but that's another story).
  Later however some request and proposals and patches were coming in.
  Among the first extensions was the recursive zzip_open magic. In
  the first instance, the library did just do as described above:
  a file-path of "this/test/README" might be a zip-file known as
  "this/test.zip" containing a compressed file "README". But if 
  there is neither a real file "this/test/README" and no real
  zip-file "this/test.zip" then the call would have failed but
  know the zzip_open call will recursivly check the parent
  directories - so it can now find a zip-file "this.zip" which
  contains a file-part "test/README". 
  This dissolves the original meaning of a ZZIP_DIR and it has lead 
  to some confusion later on - you can not create a DIRENT-like handle
  for "this/test/" being within a "test.zip" file. And actually, I did
  never see a reason to implement it so far (open "this.zip" and set
  an initial subpath of "test" and let zzip_readdir skip all entries
  that do not start with "test/"). This is left for excercie ;-)
 Extras 
  The next requests circulated around other file-extensions to 
  automagically look inside filetypes that have zip-format too but 
  carry other fileextensions - most famous might be the ".PK3"
  files of ID's Quake game. There have been a number of these
  requests and in a lot of cases it dawned to me that those guys
  may have overlooked the zzip_dir_open functions to travel
  through documents of zipformat under any name - that is that the
  "magic" was not actually needed but they just wanted to read
  files in zipformat with the zziplib.
  Other requests circulated around encryption but I did reject
  those bluntly, always. Instead there have been always examples
  for doing some obfuscation around the zip-format so that the
  stock zip/unzip tools do not recognize them but a game
  software developer can pack/unpack his AI scripts and bitmaps
  into such a zipformat-like file.
  After some dead-end patches (being shipped along with the
  zziplib as configure-time compile-options - greetings to
  Lutz Sammer and Andreas Schiffler), the general approach 
  of _ext_io came up, and finally implemented (greetings go
  to Mike Nordell). The _open()-calls do now each have a
  cousin of _open_ext_io() with two/three additional arguments
  being a set of extensions to loop through our magic testing,
  a callback-handler plugin-table for obfuscation-means, 
  and (often) a bit-mask for extra-options - this bitmask even
  has "PREFERZIP" and "ONLYZIP" options to skip the real-file
  test magic in those zzip_*open functions.
| 
  | zzip_open(name,flags) | zzip_open_ext_io(name,flags,mode,ext,io) |  | zzip_opendir(name) | zzip_opendir_ext_io(name,mode,ext,io) |  | zzip_dir_open(name,errp) | zzip_dir_open_ext_io(name,errp,ext,io) |  | zzip_dir_fdopen(fd,errp) | zzip_dir_fdopen_ext_io(fd,errp,ext,io) |  | zzip_file_open(dir,name,mode) | zzip_file_open_ext_io(dir,name,mode,ext,io) |  | 
  Oh, and note that the mode,ext,io extras are memorized 
  in the respecitive ZZIP_DIR handle attached, so each
  of the other calls like zzip_file_open()
  and zzip_read() will be using them. There
  are a few helper routines to help setup a new io-plugin
  where the init_io will currently just memcopy the
  default_io entries into the user-supplied plugin-struct.
| 
  | zzip_init_io | the recommended way to do things |  | zzip_get_default_io | used internally whenever you supply a null
                     for the io-argument of a _ext_io()-call |  | zzip_get_default_ext | used internally but not exported |  | 
  And last some stdio-like replacements were build but these
  happen to be actually just small wrappers around the other
  posix-like magic-calls. It just offers some convenience
  since wrappers like "SDL_rwops" tend to use a stringised
  open-mode - and I took the occasion to fold the zzip-bits
  for the _ext_io-calls right in there recognized via 
  special extensions to the openmode-string of zzip_fopen().
| 
  | zzip_fopen | convert stringmode and call zzip_open_ext_io |  | zzip_fread | slower way to say zzip_read |  | zzip_fclose | a synonym of zzip_close |  | 
  For some reason, people did need the full set of function-calls()
  to be working on zzip-wrappers too, so here they are - if the
  ZZIP_FILE instance did wrap a real file, then the real posix-call
  will be used, otherwise it is simulated on the compressed stream
  with a zip-contained file - especially seek() can be 
  a slow operation:
  if the new point is later then just read out more bytes till we
  hit that position but if it is an earlier point then rewind to the
  beginning of the compressed data and start reading/decompression
  until the position is met.
| 
  | zzip_rewind | magic for rewind() |  | zzip_tell | magic for tell() |  | zzip_seek | magic for seek() |  | 
  And last not least, there are few informative functions to
  use function-calls to read parts of the opaque structures
  of zzip-objects and their zzip-factory.
| 
  | zzip_dir_stat | a stat()-like thing on a file within a ZZIP_DIR |  | zzip_dir_real | check if ZZIP_DIR wraps a stat'able posix-dirent |  | zzip_file_real | check if ZZIP_FILE wraps a stat'able posix-file |  | zzip_realdir | if zzip_dir_real then return the posix-dirent |  | zzip_realfd | if zzip_file_real then return the posix-file |  | zzip_dirhandle | the attached ZZIP_DIR of compressed ZZIP_FILE |  | zzip_dirfd | the attached posix-file of ZZIP_DIR zip-file |  | zzip_set_error | set the last ZZIP_DIR error-code |  | zzip_error | get the last ZZIP_DIR error-code |  | zzip_strerror | convert a zzip_error into a readable string |  | zzip_strerror_of | combine both above zzip_strerror of zzip_error |  | zzip_errno | helper to wrap a zzip-error to a posix-errno |  | zzip_compr_str | helper to wrap a compr-number to a readable string |  | zzip_dir_free | internally called by zzip_dir_close if the ref-count 
                     of the ZZIP_DIR has gone zero |  | zzip_freopen | to reuse the ZZIP_DIR from another ZZIP_FILE so it does
                     not need to be parsed again |  | zzip_open_shared_io | the ext/io cousin but it does not close the old ZZIP_FILE
                     and instead just shares the ZZIP_DIR if possible |  |