zip_source rototil checkpoint commit -- XXX does not work yet
- Add functions for each command, use these exclusively.
- Put logic for layered sources into these functions.
- New constructor for layered sources, and new callback signature.
- Use sources for compression in zip_close.
- zip_source_crc can now validate, returning error on EOF for crc/size mismatch.

Known bugs:
- deflate uncompressing resulst in zlib data error.
- deflate compressing gets in tight loop on EOF

--HG--
branch : HEAD
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 7aa49ad..f4f6d65 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -55,14 +55,20 @@
 	zip_set_default_password.c \
 	zip_set_file_comment.c \
 	zip_source_buffer.c \
-	zip_source_call.c \
+	zip_source_close.c \
 	zip_source_crc.c \
 	zip_source_deflate.c \
+	zip_source_error.c \
 	zip_source_file.c \
 	zip_source_filep.c \
 	zip_source_free.c \
 	zip_source_function.c \
+	zip_source_layered.c \
+	zip_source_open.c \
 	zip_source_pkware.c \
+	zip_source_pop.c \
+	zip_source_read.c \
+	zip_source_stat.c \
 	zip_source_zip.c \
 	zip_set_name.c \
 	zip_stat.c \
diff --git a/lib/zip.h b/lib/zip.h
index 9a44fdf..4863e22 100644
--- a/lib/zip.h
+++ b/lib/zip.h
@@ -168,9 +168,11 @@
     ZIP_SOURCE_CLOSE,	/* reading is done */
     ZIP_SOURCE_STAT,	/* get meta information */
     ZIP_SOURCE_ERROR,	/* get error information */
-    ZIP_SOURCE_FREE	/* cleanup and free resources */
+    ZIP_SOURCE_FREE,	/* cleanup and free resources */
 };
 
+#define ZIP_SOURCE_ERR_LOWER	-2
+
 #define ZIP_STAT_NAME			0x0001
 #define ZIP_STAT_INDEX			0x0002
 #define ZIP_STAT_SIZE			0x0004
@@ -198,6 +200,9 @@
 
 typedef zip_int64_t (*zip_source_callback)(void *, void *, zip_uint64_t,
 					   enum zip_source_cmd);
+typedef zip_int64_t (*zip_source_layered_callback)(struct zip_source *, void *,
+						   void *, zip_uint64_t,
+						   enum zip_source_cmd);
 typedef struct zip_source *(*zip_compression_implementation)(struct zip *,
 						     struct zip_source *,
 						     zip_uint16_t, int);
@@ -250,12 +255,13 @@
 				    const char *, int);
 ZIP_EXTERN struct zip_source *zip_source_buffer(struct zip *, const void *,
 						zip_uint64_t, int);
-ZIP_EXTERN zip_int64_t zip_source_call(struct zip_source *, void *,
-				       zip_uint64_t, enum zip_source_cmd);
-ZIP_EXTERN struct zip_source *zip_source_crc(struct zip *, struct zip_source *);
+ZIP_EXTERN int zip_source_close(struct zip_source *);
+ZIP_EXTERN struct zip_source *zip_source_crc(struct zip *, struct zip_source *,
+					     int);
 ZIP_EXTERN struct zip_source *zip_source_deflate(struct zip *,
 						 struct zip_source *,
 						 zip_uint16_t, int);
+ZIP_EXTERN void zip_source_error(struct zip_source *, int *, int *);
 ZIP_EXTERN struct zip_source *zip_source_file(struct zip *, const char *,
 					      zip_uint64_t, zip_int64_t);
 ZIP_EXTERN struct zip_source *zip_source_filep(struct zip *, FILE *,
@@ -263,10 +269,19 @@
 ZIP_EXTERN void zip_source_free(struct zip_source *);
 ZIP_EXTERN struct zip_source *zip_source_function(struct zip *,
 						  zip_source_callback, void *);
+ZIP_EXTERN struct zip_source *zip_source_layered(struct zip *,
+						 struct zip_source *,
+						 zip_source_layered_callback,
+						 void *);
+ZIP_EXTERN int zip_source_open(struct zip_source *);
 ZIP_EXTERN struct zip_source *zip_source_pkware(struct zip *,
 						struct zip_source *,
 						zip_uint16_t, int,
 						const char *);
+ZIP_EXTERN struct zip_source *zip_source_pop(struct zip_source *);
+ZIP_EXTERN zip_int64_t zip_source_read(struct zip_source *, void *,
+				       zip_uint64_t);
+ZIP_EXTERN int zip_source_stat(struct zip_source *, struct zip_stat *);
 ZIP_EXTERN struct zip_source *zip_source_zip(struct zip *, struct zip *,
 					     zip_uint64_t, int,
 					     zip_uint64_t, zip_int64_t);
diff --git a/lib/zip_close.c b/lib/zip_close.c
index 5b45623..f6474b9 100644
--- a/lib/zip_close.c
+++ b/lib/zip_close.c
@@ -44,12 +44,9 @@
 
 static int add_data(struct zip *, struct zip_source *, struct zip_dirent *,
 		    FILE *);
-static int add_data_comp(struct zip_source *, struct zip_stat *,
-			 FILE *, struct zip_error *);
-static int add_data_uncomp(struct zip *, struct zip_source *,
-			   struct zip_stat *, FILE *);
-static void ch_set_error(struct zip_error *, struct zip_source *);
 static int copy_data(FILE *, off_t, FILE *, struct zip_error *);
+static int copy_source(struct zip *, struct zip_source *, struct zip_stat *,
+		       FILE *);
 static int write_cdir(struct zip *, struct zip_cdir *, FILE *);
 static int _zip_cdir_set_comment(struct zip_cdir *, struct zip *);
 static char *_zip_create_temp_output(struct zip *, FILE **);
@@ -237,8 +234,13 @@
 
 	    if (add_data(za, zs ? zs : za->entry[i].source, &de, out) < 0) {
 		error = 1;
+		if (zs)
+		    zip_source_free(zs);
 		break;
 	    }
+	    if (zs)
+		zip_source_free(zs);
+	    
 	    cd->entry[j].last_mod = de.last_mod;
 	    cd->entry[j].comp_method = de.comp_method;
 	    cd->entry[j].comp_size = de.comp_size;
@@ -318,15 +320,12 @@
 {
     off_t offstart, offend;
     struct zip_stat st;
+    struct zip_source *s2;
+    zip_compression_implementation comp_impl;
+    int ret;
     
-    if (zip_source_call(src, &st, sizeof(st),
-			ZIP_SOURCE_STAT) < (zip_int64_t)sizeof(st)) {
-	ch_set_error(&za->error, src);
-	return -1;
-    }
-
-    if (zip_source_call(src, NULL, 0, ZIP_SOURCE_OPEN) < 0) {
-	ch_set_error(&za->error, src);
+    if (zip_source_stat(src, &st) < 0) {
+	_zip_error_set_from_source(&za->error, src);
 	return -1;
     }
 
@@ -335,19 +334,39 @@
     if (_zip_dirent_write(de, ft, 1, &za->error) < 0)
 	return -1;
 
-    if (st.comp_method != ZIP_CM_STORE) {
-	if (add_data_comp(src, &st, ft, &za->error) < 0)
+    /* XXX: deflate 0-byte files for torrentzip? */
+    if (((st.valid & ZIP_STAT_COMP_METHOD) == 0
+	 || st.comp_method == ZIP_CM_STORE)
+	&& ((st.valid & ZIP_STAT_SIZE) == 0 || st.size != 0)) {
+	comp_impl = NULL;
+	if ((comp_impl=zip_get_compression_implementation(ZIP_CM_DEFLATE))
+	    == NULL) {
+	    _zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
 	    return -1;
+	}
+	if ((s2=comp_impl(za, src, ZIP_CM_DEFLATE, ZIP_CODEC_ENCODE))
+	    == NULL) {
+	    /* XXX: set error? */
+	    zip_source_free(src);
+	    return -1;
+	}
     }
-    else {
-	if (add_data_uncomp(za, src, &st, ft) < 0)
-	    return -1;
+    else
+	s2 = src;
+
+    ret = copy_source(za, s2, &st, ft);
+
+    while (s2 != src) {
+	if ((s2=zip_source_pop(s2)) == NULL) {
+	    /* XXX: set erorr */
+	    ret = -1;
+	    break;
+	}
     }
 
-    if (zip_source_call(src, NULL, 0, ZIP_SOURCE_CLOSE) < 0) {
-	ch_set_error(&za->error, src);
+    if (ret < 0)
 	return -1;
-    }
+
 
     offend = ftello(ft);
 
@@ -380,133 +399,6 @@
 
 
 static int
-add_data_comp(struct zip_source *src, struct zip_stat *st, FILE *ft,
-	      struct zip_error *error)
-{
-    char buf[BUFSIZE];
-    zip_int64_t n;
-
-    st->comp_size = 0;
-    while ((n=zip_source_call(src, buf, sizeof(buf), ZIP_SOURCE_READ)) > 0) {
-	if (fwrite(buf, 1, n, ft) != (size_t)n) {
-	    _zip_error_set(error, ZIP_ER_WRITE, errno);
-	    return -1;
-	}
-	
-	st->comp_size += n;
-    }
-    if (n < 0) {
-	ch_set_error(error, src);
-	return -1;
-    }	
-
-    return 0;
-}
-
-
-
-static int
-add_data_uncomp(struct zip *za, struct zip_source *src,
-		struct zip_stat *st, FILE *ft)
-{
-    char b1[BUFSIZE], b2[BUFSIZE];
-    int end, flush, ret;
-    zip_int64_t n;
-    zip_uint64_t n2;
-    z_stream zstr;
-    int mem_level;
-
-    st->comp_method = ZIP_CM_DEFLATE;
-    st->comp_size = st->size = 0;
-    st->crc = crc32(0, NULL, 0);
-
-    zstr.zalloc = Z_NULL;
-    zstr.zfree = Z_NULL;
-    zstr.opaque = NULL;
-    zstr.avail_in = 0;
-    zstr.avail_out = 0;
-
-    if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0))
-	mem_level = TORRENT_MEM_LEVEL;
-    else
-	mem_level = MAX_MEM_LEVEL;
-
-    /* -MAX_WBITS: undocumented feature of zlib to _not_ write a zlib header */
-    deflateInit2(&zstr, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, mem_level,
-		 Z_DEFAULT_STRATEGY);
-
-    zstr.next_out = (Bytef *)b2;
-    zstr.avail_out = sizeof(b2);
-    zstr.avail_in = 0;
-
-    flush = 0;
-    end = 0;
-    while (!end) {
-	if (zstr.avail_in == 0 && !flush) {
-	    if ((n=zip_source_call(src, b1, sizeof(b1), ZIP_SOURCE_READ)) < 0) {
-		ch_set_error(&za->error, src);
-		deflateEnd(&zstr);
-		return -1;
-	    }
-	    if (n > 0) {
-		zstr.avail_in = n;
-		zstr.next_in = (Bytef *)b1;
-		st->size += n;
-		st->crc = crc32(st->crc, (Bytef *)b1, n);
-	    }
-	    else
-		flush = Z_FINISH;
-	}
-
-	ret = deflate(&zstr, flush);
-	if (ret != Z_OK && ret != Z_STREAM_END) {
-	    _zip_error_set(&za->error, ZIP_ER_ZLIB, ret);
-	    return -1;
-	}
-	
-	if (zstr.avail_out != sizeof(b2)) {
-	    n2 = sizeof(b2) - zstr.avail_out;
-	    
-	    if (fwrite(b2, 1, n2, ft) != n2) {
-		_zip_error_set(&za->error, ZIP_ER_WRITE, errno);
-		return -1;
-	    }
-	
-	    zstr.next_out = (Bytef *)b2;
-	    zstr.avail_out = sizeof(b2);
-	    st->comp_size += n2;
-	}
-
-	if (ret == Z_STREAM_END) {
-	    deflateEnd(&zstr);
-	    end = 1;
-	}
-    }
-
-    return 0;
-}
-
-
-
-static void
-ch_set_error(struct zip_error *error, struct zip_source *src)
-{
-    int e[2];
-
-    if ((zip_source_call(src, e, sizeof(e),
-			 ZIP_SOURCE_ERROR)) < (zip_int64_t)sizeof(e)) {
-	error->zip_err = ZIP_ER_INTERNAL;
-	error->sys_err = 0;
-    }
-    else {
-	error->zip_err = e[0];
-	error->sys_err = e[1];
-    }
-}
-
-
-
-static int
 copy_data(FILE *fs, off_t len, FILE *ft, struct zip_error *error)
 {
     char buf[BUFSIZE];
@@ -540,6 +432,48 @@
 
 
 static int
+copy_source(struct zip *za, struct zip_source *src, struct zip_stat *st,
+	    FILE *ft)
+{
+    char buf[BUFSIZE];
+    zip_int64_t n;
+    int ret;
+
+    if (zip_source_open(src) < 0) {
+	_zip_error_set_from_source(&za->error, src);
+	return -1;
+    }
+
+    st->comp_size = 0;
+    ret = 0;
+    while ((n=zip_source_read(src, buf, sizeof(buf))) > 0) {
+	if (fwrite(buf, 1, n, ft) != (size_t)n) {
+	    _zip_error_set(&za->error, ZIP_ER_WRITE, errno);
+	    ret = -1;
+	    break;
+	}
+	
+	st->comp_size += n;
+    }
+    
+    if (n < 0) {
+	if (ret == 0)
+	    _zip_error_set_from_source(&za->error, src);
+	ret = -1;
+    }	
+
+    if (zip_source_close(src) < 0) {
+	if (ret == 0)
+	    _zip_error_set_from_source(&za->error, src);
+	ret = -1;
+    }
+    
+    return ret;
+}
+
+
+
+static int
 write_cdir(struct zip *za, struct zip_cdir *cd, FILE *out)
 {
     off_t offset;
diff --git a/lib/zip_error.c b/lib/zip_error.c
index 59aeea8..b8d907a 100644
--- a/lib/zip_error.c
+++ b/lib/zip_error.c
@@ -1,6 +1,6 @@
 /*
   zip_error.c -- struct zip_error helper functions
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
   The authors can be contacted at <libzip@nih.at>
@@ -105,8 +105,8 @@
 void
 _zip_error_set_from_source(struct zip_error *err, struct zip_source *src)
 {
-    int e[2];
+    int ze, se;
     
-    zip_source_call(src, e, sizeof(e), ZIP_SOURCE_ERROR);
-    _zip_error_set(err, e[0], e[1]);
+    zip_source_error(src, &ze, &se);
+    _zip_error_set(err, ze, se);
 }
diff --git a/lib/zip_fopen_index_encrypted.c b/lib/zip_fopen_index_encrypted.c
index 142976c..aab8955 100644
--- a/lib/zip_fopen_index_encrypted.c
+++ b/lib/zip_fopen_index_encrypted.c
@@ -132,7 +132,7 @@
 	}
 	if ((flags & ZIP_FL_COMPRESSED) == 0
 	    || st.comp_method == ZIP_CM_STORE ) {
-	    if ((s2=zip_source_crc(za, src)) == NULL) {
+	    if ((s2=zip_source_crc(za, src, 1)) == NULL) {
 		zip_source_free(src);
 		/* XXX: set error (how?) */
 		return NULL;
@@ -141,13 +141,8 @@
 	}
     }
 
-    if (zip_source_call(src, NULL, 0, ZIP_SOURCE_OPEN) < 0) {
-	int e[2];
-	
-	if (zip_source_call(src, e, sizeof(e), ZIP_SOURCE_ERROR) < 0)
-	    _zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
-	else
-	    _zip_error_set(&za->error, e[0], e[1]);
+    if (zip_source_open(src) < 0) {
+	_zip_error_set_from_source(&za->error, src);
 	zip_source_free(src);
 	return NULL;
     }
diff --git a/lib/zip_fread.c b/lib/zip_fread.c
index aab0097..8235c6d 100644
--- a/lib/zip_fread.c
+++ b/lib/zip_fread.c
@@ -56,31 +56,10 @@
     if ((zf->eof) || (toread == 0))
 	return 0;
 
-    if ((n=zip_source_call(zf->src, outbuf, toread, ZIP_SOURCE_READ)) < 0) {
+    if ((n=zip_source_read(zf->src, outbuf, toread)) < 0) {
 	_zip_error_set_from_source(&zf->error, zf->src);
 	return -1;
     }
 
-    if (n == 0) {
-	struct zip_stat st;
-
-	if (zip_source_call(zf->src, &st, sizeof(st), ZIP_SOURCE_STAT) < 0) {
-	    _zip_error_set_from_source(&zf->error, zf->src);
-	    return -1;
-	}
-
-	zf->eof = 1;
-
-	if ((st.valid & ZIP_STAT_SIZE) && zf->size != st.size) {
-	    _zip_error_set(&zf->error, ZIP_ER_INCONS, 0);
-	    return -1;
-	}
-
-	if ((st.valid & ZIP_STAT_CRC) && zf->crc != st.crc) {
-	    _zip_error_set(&zf->error, ZIP_ER_CRC, 0);
-	    return -1;
-	}
-    }
-
     return n;
 }
diff --git a/lib/zip_source_buffer.c b/lib/zip_source_buffer.c
index eee9138..af8bf19 100644
--- a/lib/zip_source_buffer.c
+++ b/lib/zip_source_buffer.c
@@ -126,9 +126,10 @@
 	    zip_stat_init(st);
 	    st->mtime = z->mtime;
 	    st->size = z->end - z->data;
+	    st->comp_size = st->size;
 	    st->comp_method = ZIP_CM_STORE;
 	    st->encryption_method = ZIP_EM_NONE;
-	    st->valid = ZIP_STAT_MTIME|ZIP_STAT_SIZE
+	    st->valid = ZIP_STAT_MTIME|ZIP_STAT_SIZE|ZIP_STAT_COMP_SIZE
 		|ZIP_STAT_COMP_METHOD|ZIP_STAT_ENCRYPTION_METHOD;
 	    
 	    return sizeof(*st);
diff --git a/lib/zip_source_call.c b/lib/zip_source_close.c
similarity index 71%
copy from lib/zip_source_call.c
copy to lib/zip_source_close.c
index 0454e0a..a889b97 100644
--- a/lib/zip_source_call.c
+++ b/lib/zip_source_close.c
@@ -1,5 +1,5 @@
 /*
-  zip_source_deflate.c -- deflate (de)compressoin routines
+  zip_source_close.c -- close zip_source (stop reading)
   Copyright (C) 2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
@@ -37,9 +37,28 @@
 
 
 
-ZIP_EXTERN zip_int64_t zip_source_call(struct zip_source *src, void *data,
-				       zip_uint64_t len,
-				       enum zip_source_cmd cmd)
+ZIP_EXTERN int
+zip_source_close(struct zip_source *src)
 {
-    return src->f(src->ud, data, len, cmd);
+    int ret;
+
+    /* XXX: Should we even allow ZIP_SOURCE_CLOSE to return an error? */
+
+    if (src->src == NULL)
+	return src->cb.f(src->ud, NULL, 0, ZIP_SOURCE_CLOSE);
+
+    ret =  src->cb.l(src->src, src->ud, NULL, 0, ZIP_SOURCE_CLOSE);
+    if (ret < 0) {
+	if (ret == ZIP_SOURCE_ERR_LOWER)
+	    src->error_source = ZIP_LES_LOWER;
+	else
+	    src->error_source = ZIP_LES_UPPER;
+    }
+
+    if (zip_source_close(src->src) < 0) {
+	src->error_source = ZIP_LES_LOWER;
+	return -1;
+    }
+    
+    return ret < 0 ? -1 : 0;
 }
diff --git a/lib/zip_source_crc.c b/lib/zip_source_crc.c
index dddd75e..7ffbfea 100644
--- a/lib/zip_source_crc.c
+++ b/lib/zip_source_crc.c
@@ -1,5 +1,5 @@
 /*
-  zip_source_crc.c -- pass-through source that calculates CRC32
+  zip_source_crc.c -- pass-through source that calculates CRC32 and size
   Copyright (C) 2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
@@ -39,18 +39,20 @@
 #include "zipint.h"
 
 struct crc {
-    struct zip_source *src;
     int eof;
+    int validate;
+    int e[2];
     zip_uint64_t size;
     zip_uint32_t crc;
 };
 
-static zip_int64_t crc_read(void *, void *, zip_uint64_t, enum zip_source_cmd);
+static zip_int64_t crc_read(struct zip_source *, void *, void *
+			    , zip_uint64_t, enum zip_source_cmd);
 
 
 
 ZIP_EXTERN struct zip_source *
-zip_source_crc(struct zip *za, struct zip_source *src)
+zip_source_crc(struct zip *za, struct zip_source *src, int validate)
 {
     struct crc *ctx;
 
@@ -64,16 +66,16 @@
 	return NULL;
     }
 
-    ctx->src = src;
+    ctx->validate = validate;
 
-
-    return zip_source_function(za, crc_read, ctx);
+    return zip_source_layered(za, src, crc_read, ctx);
 }
 
 
 
 static zip_int64_t
-crc_read(void *_ctx, void *data, zip_uint64_t len, enum zip_source_cmd cmd)
+crc_read(struct zip_source *src, void *_ctx, void *data,
+	 zip_uint64_t len, enum zip_source_cmd cmd)
 {
     struct crc *ctx;
     zip_int64_t n;
@@ -82,9 +84,6 @@
 
     switch (cmd) {
     case ZIP_SOURCE_OPEN:
-	if (zip_source_call(ctx->src, data, len, cmd) < 0)
-	    return -1;
-
 	ctx->eof = 0;
 	ctx->crc = crc32(0, NULL, 0);
 	ctx->size = 0;
@@ -95,11 +94,31 @@
 	if (ctx->eof || len == 0)
 	    return 0;
 
-	if ((n=zip_source_call(ctx->src, data, len, cmd)) < 0)
-	    return -1;
+	if ((n=zip_source_read(src, data, len)) < 0)
+	    return ZIP_SOURCE_ERR_LOWER;
 
-	if (n == 0)
+	if (n == 0) {
 	    ctx->eof = 1;
+	    if (ctx->validate) {
+		struct zip_stat st;
+
+		if (zip_source_stat(src, &st) < 0)
+		    return ZIP_SOURCE_ERR_LOWER;
+
+		if ((st.valid & ZIP_STAT_CRC) && st.crc != ctx->crc) {
+		    ctx->e[0] = ZIP_ER_CRC;
+		    ctx->e[1] = 0;
+		    
+		    return -1;
+		}
+		if ((st.valid & ZIP_STAT_SIZE) && st.size != ctx->size) {
+		    ctx->e[0] = ZIP_ER_INCONS;
+		    ctx->e[1] = 0;
+		    
+		    return -1;
+		}
+	    }
+	}
 	else {
 	    ctx->size += n;
 	    ctx->crc = crc32(ctx->crc, data, n);
@@ -107,14 +126,10 @@
 	return n;
 
     case ZIP_SOURCE_CLOSE:
-	if (zip_source_call(ctx->src, data, len, cmd) < 0)
-	    return -1;
 	return 0;
 
     case ZIP_SOURCE_STAT:
-	if (zip_source_call(ctx->src, data, len, cmd) < 0)
-	    return -1;
-	else {
+	{
 	    struct zip_stat *st;
 
 	    st = (struct zip_stat *)data;
@@ -130,15 +145,15 @@
 	return 0;
 	
     case ZIP_SOURCE_ERROR:
-	return zip_source_call(ctx->src, data, len, cmd);
+	memcpy(data, ctx->e, sizeof(ctx->e));
+	return 0;
 
     case ZIP_SOURCE_FREE:
-	zip_source_call(ctx->src, data, len, cmd);
 	free(ctx);
 	return 0;
 
     default:
-	return zip_source_call(ctx->src, data, len, cmd);
+	return -1;
     }
     
 }
diff --git a/lib/zip_source_deflate.c b/lib/zip_source_deflate.c
index 22db543..e349470 100644
--- a/lib/zip_source_deflate.c
+++ b/lib/zip_source_deflate.c
@@ -39,7 +39,6 @@
 #include "zipint.h"
 
 struct deflate {
-    struct zip_source *src;
     int e[2];
 
     int eof;
@@ -49,12 +48,15 @@
     z_stream zstr;
 };
 
-static zip_int64_t compress_read(struct deflate *, void *, zip_uint64_t);
-static zip_int64_t decompress_read(struct deflate *, void *, zip_uint64_t);
-static zip_int64_t deflate_compress(void *, void *, zip_uint64_t,
-				    enum zip_source_cmd);
-static zip_int64_t deflate_decompress(void *, void *, zip_uint64_t,
-				      enum zip_source_cmd);
+static zip_int64_t compress_read(struct zip_source *, struct deflate *,
+				 void *, zip_uint64_t);
+static zip_int64_t decompress_read(struct zip_source *, struct deflate *,
+				   void *, zip_uint64_t);
+static zip_int64_t deflate_compress(struct zip_source *, void *, void *,
+				    zip_uint64_t, enum zip_source_cmd);
+static zip_int64_t deflate_decompress(struct zip_source *, void *, void *,
+				      zip_uint64_t, enum zip_source_cmd);
+static void deflate_free(struct deflate *);
 
 
 
@@ -63,6 +65,7 @@
 		   zip_uint16_t cm, int flags)
 {
     struct deflate *ctx;
+    struct zip_source *s2;
 
     if (za == NULL || src == NULL || cm != ZIP_CM_DEFLATE) {
 	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
@@ -74,7 +77,6 @@
 	return NULL;
     }
 
-    ctx->src = src;
     ctx->e[0] = ctx->e[1] = 0;
     ctx->eof = 0;
     if (flags & ZIP_CODEC_ENCODE) {
@@ -84,15 +86,22 @@
 	    ctx->mem_level = MAX_MEM_LEVEL;
     }
 
-    return zip_source_function(za, ((flags & ZIP_CODEC_ENCODE)
-				    ? deflate_compress : deflate_decompress),
-			       ctx);
+    if ((s2=zip_source_layered(za, src,
+			       ((flags & ZIP_CODEC_ENCODE)
+				? deflate_compress : deflate_decompress),
+			       ctx)) == NULL) {
+	deflate_free(ctx);
+	return NULL;
+    }
+
+    return s2;
 }
 
 
 
 static zip_int64_t
-compress_read(struct deflate *ctx, void *data, zip_uint64_t len)
+compress_read(struct zip_source *src, struct deflate *ctx,
+	      void *data, zip_uint64_t len)
 {
     int end, ret;
     zip_int64_t n;
@@ -128,10 +137,9 @@
 		    break;
 		}
 
-		if ((n=zip_source_call(ctx->src, ctx->buffer,
-			    sizeof(ctx->buffer), ZIP_SOURCE_READ)) < 0) {
-		    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e),
-				 ZIP_SOURCE_ERROR);
+		if ((n=zip_source_read(src, ctx->buffer,
+				       sizeof(ctx->buffer))) < 0) {
+		    zip_source_error(src, ctx->e, ctx->e+1);
 		    end = 1;
 		    break;
 		}
@@ -168,7 +176,8 @@
 
 
 static zip_int64_t
-decompress_read(struct deflate *ctx, void *data, zip_uint64_t len)
+decompress_read(struct zip_source *src, struct deflate *ctx,
+		void *data, zip_uint64_t len)
 {
     int end, ret;
     zip_int64_t n;
@@ -197,10 +206,9 @@
 
 	case Z_BUF_ERROR:
 	    if (ctx->zstr.avail_in == 0) {
-		if ((n=zip_source_call(ctx->src, ctx->buffer,
-			    sizeof(ctx->buffer), ZIP_SOURCE_READ)) < 0) {
-		    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e),
-				    ZIP_SOURCE_ERROR);
+		if ((n=zip_source_read(src, ctx->buffer,
+			    sizeof(ctx->buffer))) < 0) {
+		    zip_source_error(src, ctx->e, ctx->e+1);
 		    end = 1;
 		    break;
 		}
@@ -233,8 +241,8 @@
 
 
 static zip_int64_t
-deflate_compress(void *ud, void *data, zip_uint64_t len,
-		 enum zip_source_cmd cmd)
+deflate_compress(struct zip_source *src, void *ud, void *data,
+		 zip_uint64_t len, enum zip_source_cmd cmd)
 {
     struct deflate *ctx;
     int ret;
@@ -243,7 +251,6 @@
 
     switch (cmd) {
     case ZIP_SOURCE_OPEN:
-	
 	ctx->zstr.zalloc = Z_NULL;
 	ctx->zstr.zfree = Z_NULL;
 	ctx->zstr.opaque = NULL;
@@ -257,33 +264,19 @@
 	    ctx->e[0] = ZIP_ER_ZLIB;
 	    ctx->e[1] = ret;
 	    return -1;
-
-	if (zip_source_call(ctx->src, data, len, cmd) < 0) {
-	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
-	    return -1;
 	}
 
 	return 0;
-    }
 
     case ZIP_SOURCE_READ:
-	return compress_read(ctx, data, len);
+	return compress_read(src, ctx, data, len);
 
     case ZIP_SOURCE_CLOSE:
 	deflateEnd(&ctx->zstr);
-
-	if (zip_source_call(ctx->src, data, len, cmd) < 0) {
-	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
-	    return -1;
-	}
 	return 0;
 
     case ZIP_SOURCE_STAT:
-	if (zip_source_call(ctx->src, data, len, cmd) < 0) {
-	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
-	    return -1;
-	}
-	else {
+    	{
 	    struct zip_stat *st;
 
 	    st = (struct zip_stat *)data;
@@ -300,16 +293,11 @@
 	return 0;
 
     case ZIP_SOURCE_ERROR:
-	if (len < sizeof(int)*2)
-	    return -1;
-
 	memcpy(data, ctx->e, sizeof(int)*2);
 	return sizeof(int)*2;
 
     case ZIP_SOURCE_FREE:
-	/* XXX: deflateEnd if close was not called */
-	zip_source_call(ctx->src, data, len, cmd);
-	free(ctx);
+	deflate_free(ctx);
 	return 0;
 
     default:
@@ -317,14 +305,13 @@
 	ctx->e[1] = 0;
 	return -1;
     }
-    
 }
 
 
 
 static zip_int64_t
-deflate_decompress(void *ud, void *data, zip_uint64_t len,
-		   enum zip_source_cmd cmd)
+deflate_decompress(struct zip_source *src, void *ud, void *data,
+		   zip_uint64_t len, enum zip_source_cmd cmd)
 {
     struct deflate *ctx;
     zip_int64_t n;
@@ -334,17 +321,6 @@
 
     switch (cmd) {
     case ZIP_SOURCE_OPEN:
-	if (zip_source_call(ctx->src, data, len, cmd) < 0) {
-	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
-	    return -1;
-	}
-
-	if ((n=zip_source_call(ctx->src, ctx->buffer, sizeof(ctx->buffer),
-			    ZIP_SOURCE_READ)) < 0) {
-	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
-	    return -1;
-	}
-
 	ctx->zstr.zalloc = Z_NULL;
 	ctx->zstr.zfree = Z_NULL;
 	ctx->zstr.opaque = NULL;
@@ -356,29 +332,19 @@
 	    ctx->e[0] = ZIP_ER_ZLIB;
 	    ctx->e[1] = ret;
 
-	    zip_source_call(ctx->src, NULL, 0, ZIP_SOURCE_CLOSE);
 	    return -1;
 	}
 	return 0;
 
     case ZIP_SOURCE_READ:
-	return decompress_read(ctx, data, len);
+	return decompress_read(src, ctx, data, len);
 
     case ZIP_SOURCE_CLOSE:
 	inflateEnd(&ctx->zstr);
-
-	if (zip_source_call(ctx->src, data, len, cmd) < 0) {
-	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
-	    return -1;
-	}
 	return 0;
 
     case ZIP_SOURCE_STAT:
-	if (zip_source_call(ctx->src, data, len, cmd) < 0) {
-	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
-	    return -1;
-	}
-	else {
+	{
 	    struct zip_stat *st;
 
 	    st = (struct zip_stat *)data;
@@ -398,7 +364,6 @@
 
     case ZIP_SOURCE_FREE:
 	/* XXX: inflateEnd if close was not called */
-	zip_source_call(ctx->src, data, len, cmd);
 	free(ctx);
 	return 0;
 
@@ -409,3 +374,12 @@
     }
     
 }
+
+
+
+static void
+deflate_free(struct deflate *ctx)
+{
+    /* XXX: deflateEnd if close was not called */
+    free(ctx);
+}
diff --git a/lib/zip_source_call.c b/lib/zip_source_error.c
similarity index 65%
copy from lib/zip_source_call.c
copy to lib/zip_source_error.c
index 0454e0a..db9265f 100644
--- a/lib/zip_source_call.c
+++ b/lib/zip_source_error.c
@@ -1,5 +1,5 @@
 /*
-  zip_source_deflate.c -- deflate (de)compressoin routines
+  zip_source_error.c -- get last error from zip_source
   Copyright (C) 2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
@@ -37,9 +37,50 @@
 
 
 
-ZIP_EXTERN zip_int64_t zip_source_call(struct zip_source *src, void *data,
-				       zip_uint64_t len,
-				       enum zip_source_cmd cmd)
+ZIP_EXTERN void
+zip_source_error(struct zip_source *src, int *ze, int *se)
 {
-    return src->f(src->ud, data, len, cmd);
+    int e[2];
+
+    if (src->src == NULL) {
+    }
+    else {
+	switch (src->error_source) {
+	case ZIP_LES_NONE:
+	    if (src->src == NULL) {
+		if (src->cb.f(src->ud, e, sizeof(e), ZIP_SOURCE_ERROR) < 0) {
+		    e[0] = ZIP_ER_INTERNAL;
+		    e[1] = 0;
+		}
+	    }
+	    else
+		e[0] = e[1] = 0;
+	    break;
+
+	case ZIP_LES_INVAL:
+	    e[0] = ZIP_ER_INVAL;
+	    e[1] = 0;
+	    break;
+
+	case ZIP_LES_LOWER:
+	    return zip_source_error(src->src, ze, se);
+
+	case ZIP_LES_UPPER:
+	    if (src->cb.l(src->src, src->ud, e, sizeof(e),
+			  ZIP_SOURCE_ERROR) < 0) {
+		e[0] = ZIP_ER_INTERNAL;
+		e[1] = 0;
+	    }
+	    break;
+
+	default:
+	    e[0] = ZIP_ER_INTERNAL;
+	    e[1] = 0;
+	}
+    }
+
+    if (ze)
+	*ze = e[0];
+    if (se)
+	*se = e[1];
 }
diff --git a/lib/zip_source_free.c b/lib/zip_source_free.c
index 7a1632f..1e6ffe1 100644
--- a/lib/zip_source_free.c
+++ b/lib/zip_source_free.c
@@ -1,6 +1,6 @@
 /*
   zip_source_free.c -- free zip data source
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
   The authors can be contacted at <libzip@nih.at>
@@ -40,12 +40,17 @@
 
 
 ZIP_EXTERN void
-zip_source_free(struct zip_source *source)
+zip_source_free(struct zip_source *src)
 {
-    if (source == NULL)
+    if (src == NULL)
 	return;
 
-    (void)source->f(source->ud, NULL, 0, ZIP_SOURCE_FREE);
+    if (src->src) {
+	(void)src->cb.l(src->src, src->ud, NULL, 0, ZIP_SOURCE_FREE);
+	zip_source_free(src->src);
+    }
+    else
+	(void)src->cb.f(src->ud, NULL, 0, ZIP_SOURCE_FREE);
 
-    free(source);
+    free(src);
 }
diff --git a/lib/zip_source_function.c b/lib/zip_source_function.c
index 91b183c..0e75686 100644
--- a/lib/zip_source_function.c
+++ b/lib/zip_source_function.c
@@ -1,6 +1,6 @@
 /*
   zip_source_function.c -- create zip data source from callback function
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
   The authors can be contacted at <libzip@nih.at>
@@ -52,7 +52,8 @@
 	return NULL;
     }
 
-    zs->f = zcb;
+    zs->src = NULL;
+    zs->cb.f = zcb;
     zs->ud = ud;
     
     return zs;
diff --git a/lib/zip_source_call.c b/lib/zip_source_layered.c
similarity index 75%
copy from lib/zip_source_call.c
copy to lib/zip_source_layered.c
index 0454e0a..433c77a 100644
--- a/lib/zip_source_call.c
+++ b/lib/zip_source_layered.c
@@ -1,5 +1,5 @@
 /*
-  zip_source_deflate.c -- deflate (de)compressoin routines
+  zip_source_layered.c -- create layered source
   Copyright (C) 2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
@@ -33,13 +33,30 @@
 
 
 
+#include <stdlib.h>
+
 #include "zipint.h"
 
 
 
-ZIP_EXTERN zip_int64_t zip_source_call(struct zip_source *src, void *data,
-				       zip_uint64_t len,
-				       enum zip_source_cmd cmd)
+ZIP_EXTERN struct zip_source *
+zip_source_layered(struct zip *za, struct zip_source *src,
+		   zip_source_layered_callback cb, void *ud)
 {
-    return src->f(src->ud, data, len, cmd);
+    struct zip_source *zs;
+
+    if (za == NULL)
+	return NULL;
+
+    if ((zs=(struct zip_source *)malloc(sizeof(*zs))) == NULL) {
+	_zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+	return NULL;
+    }
+
+    zs->src = src;
+    zs->cb.l = cb;
+    zs->ud = ud;
+    zs->error_source = ZIP_LES_NONE;
+    
+    return zs;
 }
diff --git a/lib/zip_source_call.c b/lib/zip_source_open.c
similarity index 72%
copy from lib/zip_source_call.c
copy to lib/zip_source_open.c
index 0454e0a..9923bda 100644
--- a/lib/zip_source_call.c
+++ b/lib/zip_source_open.c
@@ -1,5 +1,5 @@
 /*
-  zip_source_deflate.c -- deflate (de)compressoin routines
+  zip_source_open.c -- open zip_source (prepare for reading)
   Copyright (C) 2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
@@ -37,9 +37,30 @@
 
 
 
-ZIP_EXTERN zip_int64_t zip_source_call(struct zip_source *src, void *data,
-				       zip_uint64_t len,
-				       enum zip_source_cmd cmd)
+ZIP_EXTERN int
+zip_source_open(struct zip_source *src)
 {
-    return src->f(src->ud, data, len, cmd);
+    zip_int64_t ret;
+
+    if (src->src == NULL)
+	return src->cb.f(src->ud, NULL, 0, ZIP_SOURCE_OPEN);
+
+    if (zip_source_open(src->src) < 0) {
+	src->error_source = ZIP_LES_LOWER;
+	return -1;
+    }
+
+    ret = src->cb.l(src->src, src->ud, NULL, 0, ZIP_SOURCE_OPEN);
+
+    if (ret < 0) {
+	(void)zip_source_close(src->src);
+
+	if (ret == ZIP_SOURCE_ERR_LOWER)
+	    src->error_source = ZIP_LES_LOWER;
+	else
+	    src->error_source = ZIP_LES_UPPER;
+	return -1;
+    }
+    
+    return 0;
 }
diff --git a/lib/zip_source_pkware.c b/lib/zip_source_pkware.c
index 36424cd..c7736a9 100644
--- a/lib/zip_source_pkware.c
+++ b/lib/zip_source_pkware.c
@@ -39,7 +39,6 @@
 #include "zipint.h"
 
 struct trad_pkware {
-    struct zip_source *src;
     int e[2];
 
     zip_uint32_t key[3];
@@ -58,8 +57,10 @@
 
 static void decrypt(struct trad_pkware *, zip_uint8_t *,
 		    const zip_uint8_t *, zip_uint64_t, int);
-static int decrypt_header(struct trad_pkware *);
-static zip_int64_t pkware_decrypt(void *, void *, zip_uint64_t, enum zip_source_cmd);
+static int decrypt_header(struct zip_source *, struct trad_pkware *);
+static zip_int64_t pkware_decrypt(struct zip_source *, void *, void *,
+				  zip_uint64_t, enum zip_source_cmd);
+static void pkware_free(struct trad_pkware *);
 
 
 
@@ -68,6 +69,7 @@
 		  zip_uint16_t em, int flags, const char *password)
 {
     struct trad_pkware *ctx;
+    struct zip_source *s2;
 
     if (za == NULL || password == NULL || src == NULL
 	|| em != ZIP_EM_TRAD_PKWARE) {
@@ -87,7 +89,6 @@
 	return NULL;
     }
 
-    ctx->src = src;
     ctx->e[0] = ctx->e[1] = 0;
 
     ctx->key[0] = KEY0;
@@ -95,7 +96,12 @@
     ctx->key[2] = KEY2;
     decrypt(ctx, NULL, (const zip_uint8_t *)password, strlen(password), 1);
 
-    return zip_source_function(za, pkware_decrypt, ctx);
+    if ((s2=zip_source_layered(za, src, pkware_decrypt, ctx)) == NULL) {
+	pkware_free(ctx);
+	return NULL;
+    }
+
+    return s2;
 }
 
 
@@ -133,15 +139,15 @@
 
 
 static int
-decrypt_header(struct trad_pkware *ctx)
+decrypt_header(struct zip_source *src, struct trad_pkware *ctx)
 {
     zip_uint8_t header[HEADERLEN];
     struct zip_stat st;
     zip_int64_t n;
     unsigned short dostime, dosdate;
 
-    if ((n=zip_source_call(ctx->src, header, HEADERLEN, ZIP_SOURCE_READ)) < 0) {
-	zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
+    if ((n=zip_source_read(src, header, HEADERLEN)) < 0) {
+	zip_source_error(src, ctx->e, ctx->e+1);
 	return -1;
     }
     
@@ -153,9 +159,8 @@
 
     decrypt(ctx, header, header, HEADERLEN, 0);
 
-    if (zip_source_call(ctx->src, &st, sizeof(st), ZIP_SOURCE_STAT) < 0) {
+    if (zip_source_stat(src, &st) < 0) {
 	/* stat failed, skip password validation */
-
 	return 0;
     }
 
@@ -174,7 +179,8 @@
 
 
 static zip_int64_t
-pkware_decrypt(void *ud, void *data, zip_uint64_t len, enum zip_source_cmd cmd)
+pkware_decrypt(struct zip_source *src, void *ud, void *data,
+	       zip_uint64_t len, enum zip_source_cmd cmd)
 {
     struct trad_pkware *ctx;
     zip_int64_t n;
@@ -183,57 +189,41 @@
 
     switch (cmd) {
     case ZIP_SOURCE_OPEN:
-	if (zip_source_call(ctx->src, data, len, cmd) < 0) {
-	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
-	    return -1;
-	}
-	if (decrypt_header(ctx) < 0)
+	if (decrypt_header(src, ctx) < 0)
 	    return -1;
 	return 0;
 
     case ZIP_SOURCE_READ:
-	if ((n=zip_source_call(ctx->src, data, len, cmd)) < 0) {
-	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
-	    return -1;
-	}
+	if ((n=zip_source_read(src, data, len)) < 0)
+	    return ZIP_SOURCE_ERR_LOWER;
+
 	decrypt(ud, (zip_uint8_t *)data, (zip_uint8_t *)data, (zip_uint64_t)n,
 		0);
 	return n;
 
     case ZIP_SOURCE_CLOSE:
-	if (zip_source_call(ctx->src, data, len, cmd) < 0) {
-	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
-	    return -1;
-	}
 	return 0;
 
     case ZIP_SOURCE_STAT:
-	if (zip_source_call(ctx->src, data, len, cmd) < 0) {
-	    zip_source_call(ctx->src, ctx->e, sizeof(ctx->e), ZIP_SOURCE_ERROR);
-	    return -1;
-	}
-	else {
+	{
 	    struct zip_stat *st;
 
 	    st = (struct zip_stat *)data;
 
 	    st->encryption_method = ZIP_EM_NONE;
 	    st->valid |= ZIP_STAT_ENCRYPTION_METHOD;
+	    /* XXX: deduce HEADERLEN from size for uncompressed */
 	    if (st->valid & ZIP_STAT_COMP_SIZE)
 		st->comp_size -= HEADERLEN;
 	}
 	return 0;
 
     case ZIP_SOURCE_ERROR:
-	if (len < sizeof(int)*2)
-	    return -1;
-
 	memcpy(data, ctx->e, sizeof(int)*2);
 	return sizeof(int)*2;
 
     case ZIP_SOURCE_FREE:
-	zip_source_call(ctx->src, data, len, cmd);
-	free(ud);
+	pkware_free(ctx);
 	return 0;
 
     default:
@@ -242,3 +232,11 @@
 	return -1;
     }
 }
+
+
+
+static void
+pkware_free(struct trad_pkware *ctx)
+{
+    free(ctx);
+}
diff --git a/lib/zip_source_call.c b/lib/zip_source_pop.c
similarity index 82%
rename from lib/zip_source_call.c
rename to lib/zip_source_pop.c
index 0454e0a..7352251 100644
--- a/lib/zip_source_call.c
+++ b/lib/zip_source_pop.c
@@ -1,5 +1,5 @@
 /*
-  zip_source_deflate.c -- deflate (de)compressoin routines
+  zip_source_pop.c -- pop top layer from zip data source
   Copyright (C) 2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
@@ -33,13 +33,25 @@
 
 
 
+#include <stdlib.h>
+
 #include "zipint.h"
 
 
 
-ZIP_EXTERN zip_int64_t zip_source_call(struct zip_source *src, void *data,
-				       zip_uint64_t len,
-				       enum zip_source_cmd cmd)
+ZIP_EXTERN struct zip_source *
+zip_source_pop(struct zip_source *src)
 {
-    return src->f(src->ud, data, len, cmd);
+    struct zip_source *lower;
+
+    if (src == NULL)
+	return NULL;
+
+    lower = src->src;
+
+    (void)src->cb.f(src->ud, NULL, 0, ZIP_SOURCE_FREE);
+
+    free(src);
+
+    return lower;
 }
diff --git a/lib/zip_source_call.c b/lib/zip_source_read.c
similarity index 72%
copy from lib/zip_source_call.c
copy to lib/zip_source_read.c
index 0454e0a..ef7147d 100644
--- a/lib/zip_source_call.c
+++ b/lib/zip_source_read.c
@@ -1,5 +1,5 @@
 /*
-  zip_source_deflate.c -- deflate (de)compressoin routines
+  zip_source_read.c -- read data from zip_source
   Copyright (C) 2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
@@ -37,9 +37,28 @@
 
 
 
-ZIP_EXTERN zip_int64_t zip_source_call(struct zip_source *src, void *data,
-				       zip_uint64_t len,
-				       enum zip_source_cmd cmd)
+ZIP_EXTERN zip_int64_t
+zip_source_read(struct zip_source *src, void *data, zip_uint64_t len)
 {
-    return src->f(src->ud, data, len, cmd);
+    zip_int64_t ret;
+
+    if (len > ZIP_INT64_MAX || (len > 0 && data == NULL)) {
+	src->error_source = ZIP_LES_INVAL;
+	return -1;
+    }
+
+    if (src->src == NULL)
+	return src->cb.f(src->ud, data, len, ZIP_SOURCE_READ);
+
+    ret = src->cb.l(src->src, src->ud, data, len, ZIP_SOURCE_READ);
+
+    if (ret < 0) {
+	if (ret == ZIP_SOURCE_ERR_LOWER)
+	    src->error_source = ZIP_LES_LOWER;
+	else
+	    src->error_source = ZIP_LES_UPPER;
+	return -1;
+    }
+    
+    return ret;
 }
diff --git a/lib/zip_source_call.c b/lib/zip_source_stat.c
similarity index 69%
copy from lib/zip_source_call.c
copy to lib/zip_source_stat.c
index 0454e0a..2166074 100644
--- a/lib/zip_source_call.c
+++ b/lib/zip_source_stat.c
@@ -1,5 +1,5 @@
 /*
-  zip_source_deflate.c -- deflate (de)compressoin routines
+  zip_source_stat.c -- get meta information from zip_source
   Copyright (C) 2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
@@ -37,9 +37,36 @@
 
 
 
-ZIP_EXTERN zip_int64_t zip_source_call(struct zip_source *src, void *data,
-				       zip_uint64_t len,
-				       enum zip_source_cmd cmd)
+ZIP_EXTERN int
+zip_source_stat(struct zip_source *src, struct zip_stat *st)
 {
-    return src->f(src->ud, data, len, cmd);
+    zip_int64_t ret;
+
+    if (st == NULL) {
+	src->error_source = ZIP_LES_INVAL;
+	return -1;
+    }
+
+    if (src->src == NULL) {
+	if (src->cb.f(src->ud, st, sizeof(*st), ZIP_SOURCE_STAT) < 0)
+	    return -1;
+	return 0;
+    }
+
+    if (zip_source_stat(src->src, st) < 0) {
+	src->error_source = ZIP_LES_LOWER;
+	return -1;
+    }
+
+    ret = src->cb.l(src->src, src->ud, st, sizeof(*st), ZIP_SOURCE_STAT);
+
+    if (ret < 0) {
+	if (ret == ZIP_SOURCE_ERR_LOWER)
+	    src->error_source = ZIP_LES_LOWER;
+	else
+	    src->error_source = ZIP_LES_UPPER;
+	return -1;
+    }
+    
+    return 0;
 }
diff --git a/lib/zip_stat_index.c b/lib/zip_stat_index.c
index e4d84a2..0e15b55 100644
--- a/lib/zip_stat_index.c
+++ b/lib/zip_stat_index.c
@@ -54,8 +54,7 @@
 
     if ((flags & ZIP_FL_UNCHANGED) == 0
 	&& ZIP_ENTRY_DATA_CHANGED(za->entry+index)) {
-	if (zip_source_call(za->entry[index].source,
-			    st, sizeof(*st), ZIP_SOURCE_STAT) < 0) {
+	if (zip_source_stat(za->entry[index].source, st) < 0) {
 	    _zip_error_set(&za->error, ZIP_ER_CHANGED, 0);
 	    return -1;
 	}
diff --git a/lib/zip_unchange_data.c b/lib/zip_unchange_data.c
index 0249ba5..431f2d0 100644
--- a/lib/zip_unchange_data.c
+++ b/lib/zip_unchange_data.c
@@ -41,8 +41,7 @@
 _zip_unchange_data(struct zip_entry *ze)
 {
     if (ze->source) {
-	(void)ze->source->f(ze->source->ud, NULL, 0, ZIP_SOURCE_FREE);
-	free(ze->source);
+	zip_source_free(ze->source);
 	ze->source = NULL;
     }
     
diff --git a/lib/zipint.h b/lib/zipint.h
index decd334..a5363db 100644
--- a/lib/zipint.h
+++ b/lib/zipint.h
@@ -88,10 +88,9 @@
 enum zip_state { ZIP_ST_UNCHANGED, ZIP_ST_DELETED, ZIP_ST_REPLACED,
 		 ZIP_ST_ADDED, ZIP_ST_RENAMED };
 
-/* constants for struct zip_file's member flags */
+/* error source for layered sources */
 
-#define ZIP_ZF_EOF	1 /* EOF reached */
-#define ZIP_ZF_CRC	2 /* compute and compare CRC */
+enum zip_les { ZIP_LES_NONE, ZIP_LES_UPPER, ZIP_LES_LOWER, ZIP_LES_INVAL };
 
 /* directory entry: general purpose bit flags */
 
@@ -183,8 +182,12 @@
 
 struct zip_source {
     struct zip_source *src;
-    zip_source_callback f;
+    union {
+	zip_source_callback f;
+	zip_source_layered_callback l;
+    } cb;
     void *ud;
+    enum zip_les error_source;
 };
 
 /* entry in zip archive directory */