Implement I/O abstraction layer. Finally.
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index b145230..d6867d0 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -99,6 +99,7 @@
   zip_get_name.c
   zip_get_num_entries.c
   zip_get_num_files.c
+  zip_io_util.c
   zip_memdup.c
   zip_name_locate.c
   zip_new.c
@@ -111,8 +112,11 @@
   zip_set_file_comment.c
   zip_set_file_compression.c
   zip_set_name.c
+  zip_source_begin_write.c
   zip_source_buffer.c
+  zip_source_call.c
   zip_source_close.c
+  zip_source_commit_write.c
   zip_source_crc.c
   zip_source_deflate.c
   zip_source_error.c
@@ -120,13 +124,22 @@
   zip_source_filep.c
   zip_source_free.c
   zip_source_function.c
+  zip_source_is_deleted.c
   zip_source_layered.c
   zip_source_open.c
   zip_source_pkware.c
   zip_source_pop.c
   zip_source_read.c
+  zip_source_remove.c
+  zip_source_rollback_write.c
+  zip_source_seek.c
+  zip_source_seek_write.c
   zip_source_stat.c
+  zip_source_supports.c
+  zip_source_tell.c
+  zip_source_tell_write.c
   zip_source_window.c
+  zip_source_write.c
   zip_source_zip.c
   zip_source_zip_new.c
   zip_stat.c
@@ -161,6 +174,6 @@
 SET_TARGET_PROPERTIES(zip PROPERTIES VERSION 3.0 SOVERSION 3 )
 TARGET_LINK_LIBRARIES(zip ${ZLIB_LIBRARY})
 INSTALL(TARGETS zip
-	ARCHIVE DESTINATION lib
-	LIBRARY DESTINATION lib)
+  ARCHIVE DESTINATION lib
+  LIBRARY DESTINATION lib)
 #CREATE_LIBTOOL_FILE(zip lib)
diff --git a/lib/Makefile.am b/lib/Makefile.am
index c258f0c..b76d897 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -57,6 +57,7 @@
 	zip_get_num_entries.c \
 	zip_get_num_files.c \
 	zip_get_name.c \
+	zip_io_util.c \
 	zip_memdup.c \
 	zip_name_locate.c \
 	zip_new.c \
@@ -69,8 +70,11 @@
 	zip_set_file_comment.c \
 	zip_set_file_compression.c \
 	zip_set_name.c \
+	zip_source_begin_write.c \
 	zip_source_buffer.c \
+	zip_source_call.c \
 	zip_source_close.c \
+	zip_source_commit_write.c \
 	zip_source_crc.c \
 	zip_source_deflate.c \
 	zip_source_error.c \
@@ -78,13 +82,22 @@
 	zip_source_filep.c \
 	zip_source_free.c \
 	zip_source_function.c \
+	zip_source_is_deleted.c \
 	zip_source_layered.c \
 	zip_source_open.c \
 	zip_source_pkware.c \
 	zip_source_pop.c \
 	zip_source_read.c \
+	zip_source_remove.c \
+	zip_source_rollback_write.c \
+	zip_source_seek.c \
+	zip_source_seek_write.c \
 	zip_source_stat.c \
+	zip_source_supports.c \
+	zip_source_tell.c \
+	zip_source_tell_write.c \
 	zip_source_window.c \
+	zip_source_write.c \
 	zip_source_zip.c \
 	zip_source_zip_new.c \
 	zip_stat.c \
diff --git a/lib/zip.h b/lib/zip.h
index 644af2f..3b51208 100644
--- a/lib/zip.h
+++ b/lib/zip.h
@@ -51,6 +51,9 @@
 
 #ifdef __cplusplus
 extern "C" {
+#if 0
+} /* fix autoindent */
+#endif
 #endif
 
 #include <zipconf.h>
@@ -65,6 +68,7 @@
 #define ZIP_EXCL             2
 #define ZIP_CHECKCONS        4
 #define ZIP_TRUNCATE         8
+#define ZIP_RDONLY          16
 
 
 /* flags for zip_name_locate, zip_fopen, zip_stat, ... */
@@ -121,7 +125,7 @@
 #define ZIP_ER_MEMORY        14  /* N Malloc failure */
 #define ZIP_ER_CHANGED       15  /* N Entry has been changed */
 #define ZIP_ER_COMPNOTSUPP   16  /* N Compression method not supported */
-#define ZIP_ER_EOF           17  /* N Premature EOF */
+#define ZIP_ER_EOF           17  /* N Premature end of file */
 #define ZIP_ER_INVAL         18  /* N Invalid argument */
 #define ZIP_ER_NOZIP         19  /* N Not a zip archive */
 #define ZIP_ER_INTERNAL      20  /* N Internal error */
@@ -132,6 +136,9 @@
 #define ZIP_ER_RDONLY        25  /* N Read-only archive */ 
 #define ZIP_ER_NOPASSWD      26  /* N No password provided */
 #define ZIP_ER_WRONGPASSWD   27  /* N Wrong password provided */
+#define ZIP_ER_OPNOTSUPP     28  /* N Operation not supported */
+#define ZIP_ER_INUSE         29  /* N Resource still in use */
+#define ZIP_ER_TELL          30  /* S Tell error */
 
 /* type of system error value */
 
@@ -205,15 +212,41 @@
 
 
 enum zip_source_cmd {
-    ZIP_SOURCE_OPEN,	/* prepare for reading */
-    ZIP_SOURCE_READ, 	/* read data */
-    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_OPEN,            /* prepare for reading */
+    ZIP_SOURCE_READ,            /* read data */
+    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_SEEK,            /* set position for reading */
+    ZIP_SOURCE_TELL,            /* get read position */
+    ZIP_SOURCE_BEGIN_WRITE,     /* prepare for writing */
+    ZIP_SOURCE_COMMIT_WRITE,    /* writing is done */
+    ZIP_SOURCE_ROLLBACK_WRITE,  /* discard written changes */
+    ZIP_SOURCE_WRITE,           /* write data */
+    ZIP_SOURCE_SEEK_WRITE,      /* set position for writing */
+    ZIP_SOURCE_TELL_WRITE,      /* get write position */
+    ZIP_SOURCE_SUPPORTS,        /* check whether source supports command */
+    ZIP_SOURCE_REMOVE           /* remove file */
 };
+typedef enum zip_source_cmd zip_source_cmd_t;
 
-#define ZIP_SOURCE_ERR_LOWER	-2
+/* for use by sources */
+struct zip_source_args_seek {
+    zip_int64_t offset;
+    int whence;
+};
+typedef struct zip_source_args_seek zip_source_args_seek_t;
+#define ZIP_SOURCE_GET_ARGS(type, data, len, error) ((len) < sizeof(type) ? zip_error_set((error), ZIP_ER_INVAL, 0), NULL : (type *)(data))
+
+
+/* error information */
+/* use zip_error_*() to access */
+struct zip_error {
+    int zip_err;	/* libzip error code (ZIP_ER_*) */
+    int sys_err;	/* copy of errno (E*) or zlib error code */
+    char *str;		/* string representation or NULL */
+};
 
 #define ZIP_STAT_NAME			0x0001u
 #define ZIP_STAT_INDEX			0x0002u
@@ -242,6 +275,12 @@
 struct zip_file;
 struct zip_source;
 
+typedef struct zip zip_t;
+typedef struct zip_error zip_error_t;
+typedef struct zip_file zip_file_t;
+typedef struct zip_source zip_source_t;
+typedef struct zip_stat zip_stat_t;
+
 typedef zip_uint32_t zip_flags_t;    
 
 typedef zip_int64_t (*zip_source_callback)(void *, void *, zip_uint64_t,
@@ -249,13 +288,16 @@
 
 
 #ifndef ZIP_DISABLE_DEPRECATED
-ZIP_EXTERN zip_int64_t zip_add(struct zip *, const char *, struct zip_source *); /* use zip_file_add */
+ZIP_EXTERN zip_int64_t zip_add(struct zip *, const char *, zip_source_t *); /* use zip_file_add */
 ZIP_EXTERN zip_int64_t zip_add_dir(struct zip *, const char *); /* use zip_dir_add */
 ZIP_EXTERN const char *zip_get_file_comment(struct zip *, zip_uint64_t, int *, int); /* use zip_file_get_comment */
 ZIP_EXTERN int zip_get_num_files(struct zip *);  /* use zip_get_num_entries instead */
 ZIP_EXTERN int zip_rename(struct zip *, zip_uint64_t, const char *); /* use zip_file_rename */
-ZIP_EXTERN int zip_replace(struct zip *, zip_uint64_t, struct zip_source *); /* use zip_file_replace */
+ZIP_EXTERN int zip_replace(struct zip *, zip_uint64_t, zip_source_t *); /* use zip_file_replace */
 ZIP_EXTERN int zip_set_file_comment(struct zip *, zip_uint64_t, const char *, int); /* use zip_file_set_comment */
+ZIP_EXTERN int zip_error_get_sys_type(int); /* use zip_error_system_type */
+ZIP_EXTERN void zip_error_get(struct zip *, int *, int *); /* use zip_get_error, zip_error_code_zip / zip_error_code_system */
+ZIP_EXTERN void zip_file_error_get(struct zip_file *, int *, int *); /* use zip_file_get_error, zip_error_code_zip / zip_error_code_system */
 #endif
 
 ZIP_EXTERN int zip_archive_set_tempdir(struct zip *, const char *);
@@ -263,15 +305,23 @@
 ZIP_EXTERN int zip_delete(struct zip *, zip_uint64_t);
 ZIP_EXTERN zip_int64_t zip_dir_add(struct zip *, const char *, zip_flags_t);
 ZIP_EXTERN void zip_discard(struct zip *);
+
+ZIP_EXTERN struct zip_error *zip_get_error(struct zip *);
 ZIP_EXTERN void zip_error_clear(struct zip *);
-ZIP_EXTERN void zip_error_get(struct zip *, int *, int *);
-ZIP_EXTERN int zip_error_get_sys_type(int);
+ZIP_EXTERN int zip_error_code_zip(const struct zip_error *);
+ZIP_EXTERN int zip_error_code_system(const struct zip_error *);
+ZIP_EXTERN void zip_error_fini(struct zip_error *);
+ZIP_EXTERN void zip_error_init(struct zip_error *);
+ZIP_EXTERN void zip_error_set(zip_error_t *, int, int);
+ZIP_EXTERN const char *zip_error_strerror(struct zip_error *);
+ZIP_EXTERN int zip_error_system_type(const struct zip_error *);
+ZIP_EXTERN zip_int64_t zip_error_to_data(const zip_error_t *, void *, zip_uint64_t);
 ZIP_EXTERN int zip_error_to_str(char *, zip_uint64_t, int, int);
+
 ZIP_EXTERN int zip_fclose(struct zip_file *);
 ZIP_EXTERN struct zip *zip_fdopen(int, int, int *);
-ZIP_EXTERN zip_int64_t zip_file_add(struct zip *, const char *, struct zip_source *, zip_flags_t);
+ZIP_EXTERN zip_int64_t zip_file_add(struct zip *, const char *, zip_source_t *, zip_flags_t);
 ZIP_EXTERN void zip_file_error_clear(struct zip_file *);
-ZIP_EXTERN void zip_file_error_get(struct zip_file *, int *, int *);
 ZIP_EXTERN int zip_file_extra_field_delete(struct zip *, zip_uint64_t, zip_uint16_t, zip_flags_t);
 ZIP_EXTERN int zip_file_extra_field_delete_by_id(struct zip *, zip_uint64_t, zip_uint16_t, zip_uint16_t, zip_flags_t);
 ZIP_EXTERN int zip_file_extra_field_set(struct zip *, zip_uint64_t, zip_uint16_t, zip_uint16_t, const zip_uint8_t *, zip_uint16_t, zip_flags_t);
@@ -280,9 +330,10 @@
 ZIP_EXTERN const zip_uint8_t *zip_file_extra_field_get(struct zip *, zip_uint64_t, zip_uint16_t, zip_uint16_t *, zip_uint16_t *, zip_flags_t);
 ZIP_EXTERN const zip_uint8_t *zip_file_extra_field_get_by_id(struct zip *, zip_uint64_t, zip_uint16_t, zip_uint16_t, zip_uint16_t *, zip_flags_t);
 ZIP_EXTERN const char *zip_file_get_comment(struct zip *, zip_uint64_t, zip_uint32_t *, zip_flags_t);
+ZIP_EXTERN struct zip_error *zip_file_get_error(struct zip_file *);
 ZIP_EXTERN int zip_file_get_external_attributes(struct zip *, zip_uint64_t, zip_flags_t, zip_uint8_t *, zip_uint32_t *);
 ZIP_EXTERN int zip_file_rename(struct zip *, zip_uint64_t, const char *, zip_flags_t);
-ZIP_EXTERN int zip_file_replace(struct zip *, zip_uint64_t, struct zip_source *, zip_flags_t);
+ZIP_EXTERN int zip_file_replace(struct zip *, zip_uint64_t, zip_source_t *, zip_flags_t);
 ZIP_EXTERN int zip_file_set_comment(struct zip *, zip_uint64_t, const char *, zip_uint16_t, zip_flags_t);
 ZIP_EXTERN int zip_file_set_external_attributes(struct zip *, zip_uint64_t, zip_flags_t, zip_uint8_t, zip_uint32_t);
 ZIP_EXTERN int zip_file_set_mtime(struct zip *, zip_uint64_t, time_t, zip_flags_t);
@@ -298,16 +349,29 @@
 ZIP_EXTERN zip_int64_t zip_get_num_entries(struct zip *, zip_flags_t);
 ZIP_EXTERN zip_int64_t zip_name_locate(struct zip *, const char *, zip_flags_t);
 ZIP_EXTERN struct zip *zip_open(const char *, int, int *);
+ZIP_EXTERN struct zip *zip_open_from_source(zip_source_t *, int, struct zip_error *);
 ZIP_EXTERN int zip_set_archive_comment(struct zip *, const char *, zip_uint16_t);
 ZIP_EXTERN int zip_set_archive_flag(struct zip *, zip_flags_t, int);
 ZIP_EXTERN int zip_set_default_password(struct zip *, const char *);
 ZIP_EXTERN int zip_set_file_compression(struct zip *, zip_uint64_t, zip_int32_t, zip_uint32_t);
-ZIP_EXTERN struct zip_source *zip_source_buffer(struct zip *, const void *, zip_uint64_t, 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 *, zip_uint64_t, zip_int64_t);
-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_zip(struct zip *, struct zip *, zip_uint64_t, zip_flags_t, zip_uint64_t, zip_int64_t);
+ZIP_EXTERN zip_source_t *zip_source_buffer(struct zip *, const void *, zip_uint64_t, int);
+ZIP_EXTERN zip_source_t *zip_source_buffer_create(const void *, zip_uint64_t, int, zip_error_t *);
+ZIP_EXTERN int zip_source_close(zip_source_t *);
+ZIP_EXTERN zip_error_t *zip_source_error(zip_source_t *src);
+ZIP_EXTERN zip_source_t *zip_source_file(struct zip *, const char *, zip_uint64_t, zip_int64_t);
+ZIP_EXTERN zip_source_t *zip_source_file_create(const char *, zip_uint64_t, zip_int64_t, struct zip_error *);
+ZIP_EXTERN zip_source_t *zip_source_filep(struct zip *, FILE *, zip_uint64_t, zip_int64_t);
+ZIP_EXTERN zip_source_t *zip_source_filep_create(FILE *, zip_uint64_t, zip_int64_t, struct zip_error *);
+ZIP_EXTERN void zip_source_free(zip_source_t *);
+ZIP_EXTERN zip_source_t *zip_source_function(struct zip *, zip_source_callback, void *);
+ZIP_EXTERN zip_source_t *zip_source_function_create(zip_source_callback, void *, zip_error_t *);
+ZIP_EXTERN int zip_source_is_deleted(zip_source_t *);
+ZIP_EXTERN void zip_source_keep(zip_source_t *);
+ZIP_EXTERN zip_int64_t zip_source_make_command_bitmap(enum zip_source_cmd, ...);
+ZIP_EXTERN int zip_source_open(zip_source_t *);
+ZIP_EXTERN zip_int64_t zip_source_read(zip_source_t *, void *, zip_uint64_t);
+ZIP_EXTERN int zip_source_stat(zip_source_t *, struct zip_stat *);
+ZIP_EXTERN zip_source_t *zip_source_zip(struct zip *, struct zip *, zip_uint64_t, zip_flags_t, zip_uint64_t, zip_int64_t);
 ZIP_EXTERN int zip_stat(struct zip *, const char *, zip_flags_t, struct zip_stat *);
 ZIP_EXTERN int zip_stat_index(struct zip *, zip_uint64_t, zip_flags_t, struct zip_stat *);
 ZIP_EXTERN void zip_stat_init(struct zip_stat *);
diff --git a/lib/zip_add.c b/lib/zip_add.c
index 75cd79d..7574762 100644
--- a/lib/zip_add.c
+++ b/lib/zip_add.c
@@ -1,6 +1,6 @@
 /*
   zip_add.c -- add file via callback function
-  Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
diff --git a/lib/zip_add_dir.c b/lib/zip_add_dir.c
index d2450bf..65abceb 100644
--- a/lib/zip_add_dir.c
+++ b/lib/zip_add_dir.c
@@ -1,6 +1,6 @@
 /*
   zip_add_dir.c -- add directory
-  Copyright (C) 1999-2013 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
diff --git a/lib/zip_add_entry.c b/lib/zip_add_entry.c
index 688046b..4ead9be 100644
--- a/lib/zip_add_entry.c
+++ b/lib/zip_add_entry.c
@@ -1,6 +1,6 @@
 /*
   zip_add_entry.c -- create and init struct zip_entry
-  Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -47,10 +47,15 @@
     if (za->nentry+1 >= za->nentry_alloc) {
 	struct zip_entry *rentries;
 	zip_uint64_t nalloc = za->nentry_alloc + 16;
-        /* TODO check for overflow */
+	zip_uint64_t realloc_size = sizeof(struct zip_entry) * (size_t)nalloc;
+
+	if (sizeof(struct zip_entry) * (size_t)za->nentry_alloc > realloc_size) {
+	    zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+	    return -1;
+	}
 	rentries = (struct zip_entry *)realloc(za->entry, sizeof(struct zip_entry) * (size_t)nalloc);
 	if (!rentries) {
-	    _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+	    zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
 	    return -1;
 	}
 	za->entry = rentries;
diff --git a/lib/zip_close.c b/lib/zip_close.c
index 84ff9f9..1188aa6 100644
--- a/lib/zip_close.c
+++ b/lib/zip_close.c
@@ -1,6 +1,6 @@
 /*
   zip_close.c -- close zip archive and update changes
-  Copyright (C) 1999-2013 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -55,11 +55,10 @@
 /* max deflate size increase: size + ceil(size/16k)*5+6 */
 #define MAX_DEFLATE_SIZE_32	4293656963u
 
-static int add_data(struct zip *, struct zip_source *, struct zip_dirent *, FILE *);
-static int copy_data(FILE *, zip_uint64_t, FILE *, struct zip_error *);
-static int copy_source(struct zip *, struct zip_source *, FILE *);
-static int write_cdir(struct zip *, const struct zip_filelist *, zip_uint64_t, FILE *);
-static char *_zip_create_temp_output(struct zip *, FILE **);
+static int add_data(struct zip *, struct zip_source *, struct zip_dirent *);
+static int copy_data(struct zip *, zip_uint64_t);
+static int copy_source(struct zip *, struct zip_source *);
+static int write_cdir(struct zip *, const struct zip_filelist *, zip_uint64_t);
 static int _zip_torrentzip_cmp(const void *, const void *);
 
 
@@ -67,19 +66,12 @@
 zip_close(struct zip *za)
 {
     zip_uint64_t i, j, survivors;
+    zip_int64_t off;
     int error;
-    char *temp;
-    FILE *out;
-#ifndef _WIN32
-    mode_t mask;
-#endif
     struct zip_filelist *filelist;
-    int reopen_on_error;
     int new_torrentzip;
     int changed;
 
-    reopen_on_error = 0;
-
     if (za == NULL)
 	return -1;
 
@@ -87,17 +79,9 @@
 
     /* don't create zip files with no entries */
     if (survivors == 0) {
-	if (za->zn && ((za->open_flags & ZIP_TRUNCATE) || (changed && za->zp))) {
-	    int reopen = 0;
-	    if (za->zp) {
-		reopen = 1;
-		fclose(za->zp);
-		za->zp = NULL;
-	    }
-	    if (remove(za->zn) != 0) {
-		_zip_error_set(&za->error, ZIP_ER_REMOVE, errno);
-		if (reopen)
-		    za->zp = fopen(za->zn, "rb");
+	if ((za->open_flags & ZIP_TRUNCATE) || changed) {
+	    if (zip_source_remove(za->src) < 0) {
+		zip_error_set_from_source(&za->error, za->src);
 		return -1;
 	    }
 	}
@@ -111,7 +95,7 @@
     }
 
     if (survivors > za->nentry) {
-        _zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
+        zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
         return -1;
     }
     
@@ -136,7 +120,7 @@
 
         if (j >= survivors) {
             free(filelist);
-            _zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
+            zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
             return -1;
         }
         
@@ -146,17 +130,16 @@
     }
     if (j < survivors) {
         free(filelist);
-        _zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
+        zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
         return -1;
     }
 
-
-    if ((temp=_zip_create_temp_output(za, &out)) == NULL) {
+    if (zip_source_begin_write(za->src) < 0) {
+	zip_error_set_from_source(&za->error, za->src);
 	free(filelist);
 	return -1;
     }
     
-    
     if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0))
 	qsort(filelist, (size_t)survivors, sizeof(filelist[0]),
 	      _zip_torrentzip_cmp);
@@ -178,7 +161,7 @@
 	/* create new local directory entry */
 	if (entry->changes == NULL) {
 	    if ((entry->changes=_zip_dirent_clone(entry->orig)) == NULL) {
-                _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+                zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
                 error = 1;
                 break;
 	    }
@@ -193,8 +176,11 @@
 	if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0))
 	    _zip_dirent_torrent_normalize(entry->changes);
 
-
-	de->offset = (zip_uint64_t)ftello(out); /* TODO: check for errors */
+        if ((off = zip_source_tell_write(za->src)) < 0) {
+            error = 1;
+            break;
+        }
+        de->offset = (zip_uint64_t)off;
 
 	if (new_data) {
 	    struct zip_source *zs;
@@ -208,7 +194,7 @@
 	    }
 
 	    /* add_data writes dirent */
-	    if (add_data(za, zs ? zs : entry->source, de, out) < 0) {
+	    if (add_data(za, zs ? zs : entry->source, de) < 0) {
 		error = 1;
 		if (zs)
 		    zip_source_free(zs);
@@ -222,7 +208,7 @@
 
 	    /* when copying data, all sizes are known -> no data descriptor needed */
 	    de->bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR;
-	    if (_zip_dirent_write(de, out, ZIP_FL_LOCAL, &za->error) < 0) {
+	    if (_zip_dirent_write(za, de, ZIP_FL_LOCAL) < 0) {
 		error = 1;
 		break;
 	    }
@@ -230,12 +216,12 @@
 		error = 1;
 		break;
 	    }
-	    if ((fseeko(za->zp, (off_t)offset, SEEK_SET) < 0)) {
-		_zip_error_set(&za->error, ZIP_ER_SEEK, errno);
+	    if (zip_source_seek(za->src, (zip_int64_t)offset, SEEK_SET) < 0) {
+		zip_error_set_from_source(&za->error, za->src);
 		error = 1;
 		break;
 	    }
-	    if (copy_data(za->zp, de->comp_size, out, &za->error) < 0) {
+	    if (copy_data(za, de->comp_size) < 0) {
 		error = 1;
 		break;
 	    }
@@ -243,58 +229,34 @@
     }
 
     if (!error) {
-	if (write_cdir(za, filelist, survivors, out) < 0)
+	if (write_cdir(za, filelist, survivors) < 0)
 	    error = 1;
     }
 
     free(filelist);
 
-    if (error) {
-	fclose(out);
-	(void)remove(temp);
-	free(temp);
-	return -1;
+    if (!error) {
+	if (zip_source_commit_write(za->src) != 0) {
+	    zip_error_set_from_source(&za->error, za->src);
+	    error = 1;
+	}
     }
 
-    if (fclose(out) != 0) {
-	_zip_error_set(&za->error, ZIP_ER_CLOSE, errno);
-	(void)remove(temp);
-	free(temp);
+    if (error) {
+	zip_source_rollback_write(za->src);
 	return -1;
     }
-    
-    if (za->zp) {
-	fclose(za->zp);
-	za->zp = NULL;
-	reopen_on_error = 1;
-    }
-    if (_zip_rename(temp, za->zn) != 0) {
-	_zip_error_set(&za->error, ZIP_ER_RENAME, errno);
-	(void)remove(temp);
-	free(temp);
-	if (reopen_on_error) {
-	    /* ignore errors, since we're already in an error case */
-	    za->zp = fopen(za->zn, "rb");
-	}
-	return -1;
-    }
-#ifndef _WIN32
-    mask = umask(0);
-    umask(mask);
-    chmod(za->zn, 0666&~mask);
-#endif
 
     zip_discard(za);
-    free(temp);
     
     return 0;
 }
 
 
 static int
-add_data(struct zip *za, struct zip_source *src, struct zip_dirent *de, FILE *ft)
+add_data(struct zip *za, struct zip_source *src, struct zip_dirent *de)
 {
-    off_t offstart, offdata, offend;
+    zip_int64_t offstart, offdata, offend;
     struct zip_stat st;
     struct zip_source *s2;
     int ret;
@@ -302,7 +264,7 @@
     zip_flags_t flags;
     
     if (zip_source_stat(src, &st) < 0) {
-	_zip_error_set_from_source(&za->error, src);
+	zip_error_set_from_source(&za->error, src);
 	return -1;
     }
 
@@ -339,12 +301,13 @@
 	    de->comp_size = st.comp_size;
     }
 
-
-    offstart = ftello(ft);
+    if ((offstart = zip_source_tell_write(za->src)) < 0) {
+        return -1;
+    }
 
     /* as long as we don't support non-seekable output, clear data descriptor bit */
     de->bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR;
-    if ((is_zip64=_zip_dirent_write(de, ft, flags, &za->error)) < 0)
+    if ((is_zip64=_zip_dirent_write(za, de, flags)) < 0)
 	return -1;
 
 
@@ -354,7 +317,7 @@
 	
 	if (st.comp_method != ZIP_CM_STORE) {
 	    if ((comp_impl=_zip_get_compression_implementation(st.comp_method)) == NULL) {
-		_zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
+		zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
 		return -1;
 	    }
 	    if ((s_store=comp_impl(za, src, st.comp_method, ZIP_CODEC_DECODE)) == NULL) {
@@ -374,7 +337,7 @@
 	/* TODO: deflate 0-byte files for torrentzip? */
 	if (de->comp_method != ZIP_CM_STORE && ((st.valid & ZIP_STAT_SIZE) == 0 || st.size != 0)) {
 	    if ((comp_impl=_zip_get_compression_implementation(de->comp_method)) == NULL) {
-		_zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
+		zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
 		zip_source_pop(s_crc);
 		if (s_store != src)
 		    zip_source_pop(s_store);
@@ -393,33 +356,40 @@
     else
 	s2 = src;
 
-    offdata = ftello(ft);
-	
-    ret = copy_source(za, s2, ft);
+    if ((offdata = zip_source_tell_write(za->src)) < 0) {
+        return -1;
+    }
+
+    ret = copy_source(za, s2);
 	
     if (zip_source_stat(s2, &st) < 0)
 	ret = -1;
     
     while (s2 != src) {
-	if ((s2=zip_source_pop(s2)) == NULL) {
-	    /* TODO: set erorr */
+	zip_source_t *tmp = zip_source_pop(s2);
+
+	if (tmp == NULL) {
+	    zip_error_set_from_source(&za->error, s2);
 	    ret = -1;
 	    break;
 	}
+	s2 = tmp;
     }
 
     if (ret < 0)
 	return -1;
 
-    offend = ftello(ft);
+    if ((offend = zip_source_tell_write(za->src)) < 0) {
+        return -1;
+    }
 
-    if (fseeko(ft, offstart, SEEK_SET) < 0) {
-	_zip_error_set(&za->error, ZIP_ER_SEEK, errno);
+    if (zip_source_seek_write(za->src, offstart, SEEK_SET) < 0) {
+	zip_error_set_from_source(&za->error, za->src);
 	return -1;
     }
 
     if ((st.valid & (ZIP_STAT_COMP_METHOD|ZIP_STAT_CRC|ZIP_STAT_SIZE)) != (ZIP_STAT_COMP_METHOD|ZIP_STAT_CRC|ZIP_STAT_SIZE)) {
-	_zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
 	return -1;
     }
 
@@ -432,23 +402,23 @@
     de->comp_method = st.comp_method;
     de->crc = st.crc;
     de->uncomp_size = st.size;
-    de->comp_size = (zip_uint64_t)(offend - offdata);
+    de->comp_size = (zip_uint8_t)(offend - offdata);
 
     if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0))
 	_zip_dirent_torrent_normalize(de);
 
-    if ((ret=_zip_dirent_write(de, ft, flags, &za->error)) < 0)
+    if ((ret=_zip_dirent_write(za, de, flags)) < 0)
 	return -1;
  
     if (is_zip64 != ret) {
 	/* Zip64 mismatch between preliminary file header written before data and final file header written afterwards */
-	_zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
 	return -1;
     }
 
    
-    if (fseeko(ft, offend, SEEK_SET) < 0) {
-	_zip_error_set(&za->error, ZIP_ER_SEEK, errno);
+    if (zip_source_seek_write(za->src, offend, SEEK_SET) < 0) {
+	zip_error_set_from_source(&za->error, za->src);
 	return -1;
     }
 
@@ -457,29 +427,18 @@
 
 
 static int
-copy_data(FILE *fs, zip_uint64_t len, FILE *ft, struct zip_error *error)
+copy_data(struct zip *za, zip_uint64_t len)
 {
-    char buf[BUFSIZE];
-    size_t n, nn;
-
-    if (len == 0)
-	return 0;
+    zip_uint8_t buf[BUFSIZE];
+    size_t n;
 
     while (len > 0) {
-	nn = len > sizeof(buf) ? sizeof(buf) : len > SIZE_MAX ? SIZE_MAX : (size_t)len;
-	if ((n=fread(buf, 1, nn, fs)) == 0) {
-            if (ferror(fs)) {
-                _zip_error_set(error, ZIP_ER_READ, errno);
-                return -1;
-            }
-            else {
-                _zip_error_set(error, ZIP_ER_EOF, 0);
-                return -1;
-            }
-        }
+	n = len > sizeof(buf) ? sizeof(buf) : len;
+	if (_zip_read(za->src, buf, n, &za->error) < 0) {
+	    return -1;
+	}
 
-	if (fwrite(buf, 1, n, ft) != (size_t)n) {
-	    _zip_error_set(error, ZIP_ER_WRITE, errno);
+	if (_zip_write(za, buf, n) < 0) {
 	    return -1;
 	}
 	
@@ -491,31 +450,29 @@
 
 
 static int
-copy_source(struct zip *za, struct zip_source *src, FILE *ft)
+copy_source(struct zip *za, struct zip_source *src)
 {
-    char buf[BUFSIZE];
+    zip_uint8_t buf[BUFSIZE];
     zip_int64_t n;
     int ret;
 
     if (zip_source_open(src) < 0) {
-	_zip_error_set_from_source(&za->error, src);
+	zip_error_set_from_source(&za->error, src);
 	return -1;
     }
 
     ret = 0;
     while ((n=zip_source_read(src, buf, sizeof(buf))) > 0) {
-	if (fwrite(buf, 1, (size_t)n, ft) != (size_t)n) {
-	    _zip_error_set(&za->error, ZIP_ER_WRITE, errno);
+	if (_zip_write(za, buf, (zip_uint64_t)n) < 0) {
 	    ret = -1;
 	    break;
 	}
     }
     
     if (n < 0) {
-	if (ret == 0)
-	    _zip_error_set_from_source(&za->error, src);
+	zip_error_set_from_source(&za->error, src);
 	ret = -1;
-    }	
+    }
 
     zip_source_close(src);
     
@@ -524,38 +481,39 @@
 
 
 static int
-write_cdir(struct zip *za, const struct zip_filelist *filelist, zip_uint64_t survivors, FILE *out)
+write_cdir(struct zip *za, const struct zip_filelist *filelist, zip_uint64_t survivors)
 {
-    off_t cd_start, end;
-    zip_int64_t size;
+    zip_int64_t cd_start, end, size;
     uLong crc;
-    char buf[TORRENT_CRC_LEN+1];
+    zip_uint8_t buf[TORRENT_CRC_LEN+1];
     
-    cd_start = ftello(out);
+    if ((cd_start = zip_source_tell_write(za->src)) < 0) {
+        return -1;
+    }
 
-    if ((size=_zip_cdir_write(za, filelist, survivors, out)) < 0)
+    if ((size=_zip_cdir_write(za, filelist, survivors)) < 0) {
 	return -1;
+    }
     
-    end = ftello(out);
+    if ((end = zip_source_tell_write(za->src)) < 0) {
+        return -1;
+    }
 
     if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 0)
 	return 0;
 
-
     /* fix up torrentzip comment */
-
-    if (_zip_filerange_crc(out, cd_start, size, &crc, &za->error) < 0)
+    if (_zip_filerange_crc(za->src, (zip_uint64_t)cd_start, (zip_uint64_t)size, &crc, &za->error) < 0)
 	return -1;
 
-    snprintf(buf, sizeof(buf), "%08lX", (long)crc);
+    snprintf((char *)buf, sizeof(buf), "%08lX", (long)crc);
 
-    if (fseeko(out, end-TORRENT_CRC_LEN, SEEK_SET) < 0) {
-	_zip_error_set(&za->error, ZIP_ER_SEEK, errno);
+    if (zip_source_seek_write(za->src, end-TORRENT_CRC_LEN, SEEK_SET) < 0) {
+	zip_error_set_from_source(&za->error, za->src);
 	return -1;
     }
 
-    if (fwrite(buf, TORRENT_CRC_LEN, 1, out) != 1) {
-	_zip_error_set(&za->error, ZIP_ER_WRITE, errno);
+    if (_zip_write(za, buf, TORRENT_CRC_LEN) < 0) {
 	return -1;
     }
 
@@ -589,54 +547,6 @@
 }
 
 
-static char *
-_zip_create_temp_output(struct zip *za, FILE **outp)
-{
-    char *temp;
-    int tfd;
-    FILE *tfp;
-    
-    if (za->tempdir) {
-        if ((temp=(char *)malloc(strlen(za->tempdir)+13)) == NULL) {
-            _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
-            return NULL;
-        }
-        sprintf(temp, "%s/.zip.XXXXXX", za->tempdir);
-    }
-    else {
-        if ((temp=(char *)malloc(strlen(za->zn)+8)) == NULL) {
-            _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
-            return NULL;
-        }
-        sprintf(temp, "%s.XXXXXX", za->zn);
-    }
-
-    if ((tfd=mkstemp(temp)) == -1) {
-	_zip_error_set(&za->error, ZIP_ER_TMPOPEN, errno);
-	free(temp);
-	return NULL;
-    }
-    
-    if ((tfp=fdopen(tfd, "r+b")) == NULL) {
-	_zip_error_set(&za->error, ZIP_ER_TMPOPEN, errno);
-	close(tfd);
-	(void)remove(temp);
-	free(temp);
-	return NULL;
-    }
-
-#ifdef _WIN32
-    /*
-      According to Pierre Joye, Windows in some environments per
-      default creates text files, so force binary mode.
-    */
-    _setmode(_fileno(tfp), _O_BINARY );
-#endif
-
-    *outp = tfp;
-    return temp;
-}
-
 
 static int
 _zip_torrentzip_cmp(const void *a, const void *b)
diff --git a/lib/zip_delete.c b/lib/zip_delete.c
index db30157..668e8b9 100644
--- a/lib/zip_delete.c
+++ b/lib/zip_delete.c
@@ -1,6 +1,6 @@
 /*
   zip_delete.c -- delete file from zip archive
-  Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -39,12 +39,12 @@
 zip_delete(struct zip *za, zip_uint64_t idx)
 {
     if (idx >= za->nentry) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
     if (ZIP_IS_RDONLY(za)) {
-	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
 	return -1;
     }
 
diff --git a/lib/zip_dir_add.c b/lib/zip_dir_add.c
index d7f4ef6..a68337c 100644
--- a/lib/zip_dir_add.c
+++ b/lib/zip_dir_add.c
@@ -1,6 +1,6 @@
 /*
   zip_dir_add.c -- add directory
-  Copyright (C) 1999-2013 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -49,12 +49,12 @@
     struct zip_source *source;
 
     if (ZIP_IS_RDONLY(za)) {
-	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
 	return -1;
     }
 
     if (name == NULL) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
@@ -63,7 +63,7 @@
 
     if (name[len-1] != '/') {
 	if ((s=(char *)malloc(len+2)) == NULL) {
-	    _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+	    zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
 	    return -1;
 	}
 	strcpy(s, name);
diff --git a/lib/zip_dirent.c b/lib/zip_dirent.c
index db051b5..c20eda2 100644
--- a/lib/zip_dirent.c
+++ b/lib/zip_dirent.c
@@ -1,6 +1,6 @@
 /*
   zip_dirent.c -- read directory entry (local or central), clean dirent
-  Copyright (C) 1999-2013 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -42,7 +42,6 @@
 #include "zipint.h"
 
 static time_t _zip_d2u_time(zip_uint16_t, zip_uint16_t);
-static struct zip_string *_zip_read_string(const unsigned char **, FILE *, zip_uint16_t, int, struct zip_error *);
 static struct zip_string *_zip_dirent_process_ef_utf_8(const struct zip_dirent *, zip_uint16_t, struct zip_string *);
 static struct zip_extra_field *_zip_ef_utf8(zip_uint16_t, struct zip_string *, struct zip_error *);
 
@@ -70,7 +69,7 @@
     zip_uint64_t i;
 
     if (nentry < cd->nentry_alloc) {
-	_zip_error_set(error, ZIP_ER_INTERNAL, 0);
+	zip_error_set(error, ZIP_ER_INTERNAL, 0);
 	return -1;
     }
 
@@ -79,7 +78,7 @@
 
     if ((entry=((struct zip_entry *)
 		realloc(cd->entry, sizeof(*(cd->entry))*(size_t)nentry))) == NULL) {
-	_zip_error_set(error, ZIP_ER_MEMORY, 0);
+	zip_error_set(error, ZIP_ER_MEMORY, 0);
 	return -1;
     }
     
@@ -100,14 +99,14 @@
     zip_uint64_t i;
     
     if ((cd=(struct zip_cdir *)malloc(sizeof(*cd))) == NULL) {
-	_zip_error_set(error, ZIP_ER_MEMORY, 0);
+	zip_error_set(error, ZIP_ER_MEMORY, 0);
 	return NULL;
     }
 
     if (nentry == 0)
 	cd->entry = NULL;
     else if ((cd->entry=(struct zip_entry *)malloc(sizeof(*(cd->entry))*(size_t)nentry)) == NULL) {
-	_zip_error_set(error, ZIP_ER_MEMORY, 0);
+	zip_error_set(error, ZIP_ER_MEMORY, 0);
 	free(cd);
 	return NULL;
     }
@@ -124,17 +123,18 @@
 
 
 zip_int64_t
-_zip_cdir_write(struct zip *za, const struct zip_filelist *filelist, zip_uint64_t survivors, FILE *fp)
+_zip_cdir_write(struct zip *za, const struct zip_filelist *filelist, zip_uint64_t survivors)
 {
-    off_t off;
     zip_uint64_t offset, size;
     struct zip_string *comment;
+    zip_uint8_t buf[EOCDLEN + EOCD64LEN], *p;
+    zip_int64_t off;
     zip_uint64_t i;
     int is_zip64;
     int ret;
 
-    if ((off=ftello(fp)) < 0) {
-        _zip_error_set(&za->error, ZIP_ER_READ, errno);
+    if ((off = zip_source_tell_write(za->src)) < 0) {
+        zip_error_set_from_source(&za->error, za->src);
         return -1;
     }
     offset = (zip_uint64_t)off;
@@ -144,14 +144,14 @@
     for (i=0; i<survivors; i++) {
 	struct zip_entry *entry = za->entry+filelist[i].idx;
 
-	if ((ret=_zip_dirent_write(entry->changes ? entry->changes : entry->orig, fp, ZIP_FL_CENTRAL, &za->error)) < 0)
+	if ((ret=_zip_dirent_write(za, entry->changes ? entry->changes : entry->orig, ZIP_FL_CENTRAL)) < 0)
 	    return -1;
 	if (ret)
 	    is_zip64 = 1;
     }
 
-    if ((off=ftello(fp)) < 0) {
-        _zip_error_set(&za->error, ZIP_ER_READ, errno);
+    if ((off = zip_source_tell_write(za->src)) < 0) {
+        zip_error_set_from_source(&za->error, za->src);
         return -1;
     }
     size = (zip_uint64_t)off - offset;
@@ -159,44 +159,45 @@
     if (offset > ZIP_UINT32_MAX || survivors > ZIP_UINT16_MAX)
 	is_zip64 = 1;
 
+    p = buf;
     if (is_zip64) {
-	fwrite(EOCD64_MAGIC, 1, 4, fp);
-	_zip_write8(EOCD64LEN-12, fp);
-	_zip_write2(45, fp);
-	_zip_write2(45, fp);
-	_zip_write4(0, fp);
-	_zip_write4(0, fp);
-	_zip_write8(survivors, fp);
-	_zip_write8(survivors, fp);
-	_zip_write8(size, fp);
-	_zip_write8(offset, fp);
-
-	fwrite(EOCD64LOC_MAGIC, 1, 4, fp);
-	_zip_write4(0, fp);
-	_zip_write8(offset+size, fp);
-	_zip_write4(1, fp);
-		    
+	_zip_put_data(&p, EOCD64_MAGIC, 4);
+	_zip_put_64(&p, EOCD64LEN-12);
+	_zip_put_16(&p, 45);
+	_zip_put_16(&p, 45);
+	_zip_put_32(&p, 0);
+	_zip_put_32(&p, 0);
+	_zip_put_64(&p, survivors);
+	_zip_put_64(&p, survivors);
+	_zip_put_64(&p, size);
+	_zip_put_64(&p, offset);
+	_zip_put_data(&p, EOCD64LOC_MAGIC, 4);
+	_zip_put_32(&p, 0);
+	_zip_put_64(&p, offset+size);
+	_zip_put_32(&p, 1);
     }
-
-    /* clearerr(fp); */
-    fwrite(EOCD_MAGIC, 1, 4, fp);
-    _zip_write4(0, fp);
-    _zip_write2(survivors >= ZIP_UINT16_MAX ? ZIP_UINT16_MAX : (zip_uint16_t)survivors, fp);
-    _zip_write2(survivors >= ZIP_UINT16_MAX ? ZIP_UINT16_MAX : (zip_uint16_t)survivors, fp);
-    _zip_write4(size >= ZIP_UINT32_MAX ? ZIP_UINT32_MAX : (zip_uint32_t)size, fp);
-    _zip_write4(offset >= ZIP_UINT32_MAX ? ZIP_UINT32_MAX : (zip_uint32_t)offset, fp);
+    
+    _zip_put_data(&p, EOCD_MAGIC, 4);
+    _zip_put_32(&p, 0);
+    _zip_put_16(&p, survivors >= ZIP_UINT16_MAX ? ZIP_UINT16_MAX : (zip_uint16_t)survivors);
+    _zip_put_16(&p, survivors >= ZIP_UINT16_MAX ? ZIP_UINT16_MAX : (zip_uint16_t)survivors);
+    _zip_put_32(&p, size >= ZIP_UINT32_MAX ? ZIP_UINT32_MAX : (zip_uint32_t)size);
+    _zip_put_32(&p, offset >= ZIP_UINT32_MAX ? ZIP_UINT32_MAX : (zip_uint32_t)offset);
 
     comment = za->comment_changed ? za->comment_changes : za->comment_orig;
 
-    _zip_write2(comment ? comment->length : 0, fp);
-    if (comment)
-	fwrite(comment->raw, 1, comment->length, fp);
+    _zip_put_16(&p, comment ? comment->length : 0);
 
-    if (ferror(fp)) {
-	_zip_error_set(&za->error, ZIP_ER_WRITE, errno);
+    if (_zip_write(za, buf, (zip_uint64_t)(p-buf)) < 0) {
 	return -1;
     }
 
+    if (comment) {
+	if (_zip_write(za, comment->raw, comment->length) < 0) {
+	    return -1;
+	}
+    }
+
     return (zip_int64_t)size;
 }
 
@@ -312,7 +313,7 @@
 */
 
 int
-_zip_dirent_read(struct zip_dirent *zde, FILE *fp,
+_zip_dirent_read(struct zip_dirent *zde, struct zip_source *src,
 		 const unsigned char **bufp, zip_uint64_t *leftp, int local,
 		 struct zip_error *error)
 {
@@ -328,7 +329,7 @@
 	size = CDENTRYSIZE;
 
     if (leftp && (*leftp < size)) {
-	_zip_error_set(error, ZIP_ER_NOZIP, 0);
+	zip_error_set(error, ZIP_ER_NOZIP, 0);
 	return -1;
     }
 
@@ -337,16 +338,21 @@
 	cur = *bufp;
     }
     else {
-	/* read entry from disk */
-	if ((fread(buf, 1, size, fp)<size)) {
-	    _zip_error_set(error, ZIP_ER_READ, errno);
+	zip_int64_t n;
+
+	if ((n = zip_source_read(src, buf, size)) < 0) {
+            zip_error_set_from_source(error, src);
+	    return -1;
+	}
+	if ((zip_uint64_t)n != size) {
+	    zip_error_set(error, ZIP_ER_NOZIP, errno);
 	    return -1;
 	}
 	cur = buf;
     }
 
     if (memcmp(cur, (local ? LOCAL_MAGIC : CENTRAL_MAGIC), 4) != 0) {
-	_zip_error_set(error, ZIP_ER_NOZIP, 0);
+	zip_error_set(error, ZIP_ER_NOZIP, 0);
 	return -1;
     }
     cur += 4;
@@ -356,24 +362,24 @@
 
     _zip_dirent_init(zde);
     if (!local)
-	zde->version_madeby = _zip_read2(&cur);
+	zde->version_madeby = _zip_get_16(&cur);
     else
 	zde->version_madeby = 0;
-    zde->version_needed = _zip_read2(&cur);
-    zde->bitflags = _zip_read2(&cur);
-    zde->comp_method = _zip_read2(&cur);
+    zde->version_needed = _zip_get_16(&cur);
+    zde->bitflags = _zip_get_16(&cur);
+    zde->comp_method = _zip_get_16(&cur);
     
     /* convert to time_t */
-    dostime = _zip_read2(&cur);
-    dosdate = _zip_read2(&cur);
+    dostime = _zip_get_16(&cur);
+    dosdate = _zip_get_16(&cur);
     zde->last_mod = _zip_d2u_time(dostime, dosdate);
     
-    zde->crc = _zip_read4(&cur);
-    zde->comp_size = _zip_read4(&cur);
-    zde->uncomp_size = _zip_read4(&cur);
+    zde->crc = _zip_get_32(&cur);
+    zde->comp_size = _zip_get_32(&cur);
+    zde->uncomp_size = _zip_get_32(&cur);
     
-    filename_len = _zip_read2(&cur);
-    ef_len = _zip_read2(&cur);
+    filename_len = _zip_get_16(&cur);
+    ef_len = _zip_get_16(&cur);
     
     if (local) {
 	comment_len = 0;
@@ -382,11 +388,11 @@
 	zde->ext_attrib = 0;
 	zde->offset = 0;
     } else {
-	comment_len = _zip_read2(&cur);
-	zde->disk_number = _zip_read2(&cur);
-	zde->int_attrib = _zip_read2(&cur);
-	zde->ext_attrib = _zip_read4(&cur);
-	zde->offset = _zip_read4(&cur);
+	comment_len = _zip_get_16(&cur);
+	zde->disk_number = _zip_get_16(&cur);
+	zde->int_attrib = _zip_get_16(&cur);
+	zde->ext_attrib = _zip_get_32(&cur);
+	zde->offset = _zip_get_32(&cur);
     }
 
     zde->filename = NULL;
@@ -396,25 +402,29 @@
     size += filename_len+ef_len+comment_len;
 
     if (leftp && (*leftp < size)) {
-	_zip_error_set(error, ZIP_ER_INCONS, 0);
+	zip_error_set(error, ZIP_ER_INCONS, 0);
 	return -1;
     }
 
     if (filename_len) {
-	zde->filename = _zip_read_string(bufp ? &cur : NULL, fp, filename_len, 1, error);
-	if (!zde->filename)
+	zde->filename = _zip_read_string(bufp ? &cur : NULL, src, filename_len, 1, error);
+        if (!zde->filename) {
+            if (zip_error_code_zip(error) == ZIP_ER_EOF) {
+                zip_error_set(error, ZIP_ER_INCONS, 0);
+            }
 	    return -1;
+        }
 
 	if (zde->bitflags & ZIP_GPBF_ENCODING_UTF_8) {
 	    if (_zip_guess_encoding(zde->filename, ZIP_ENCODING_UTF8_KNOWN) == ZIP_ENCODING_ERROR) {
-		_zip_error_set(error, ZIP_ER_INCONS, 0);
+		zip_error_set(error, ZIP_ER_INCONS, 0);
 		return -1;
 	    }
 	}
     }
 
     if (ef_len) {
-	zip_uint8_t *ef = _zip_read_data(bufp ? &cur : NULL, fp, ef_len, 0, error);
+	zip_uint8_t *ef = _zip_read_data(bufp ? &cur : NULL, src, ef_len, 0, error);
 
 	if (ef == NULL)
 	    return -1;
@@ -428,13 +438,13 @@
     }
 
     if (comment_len) {
-	zde->comment = _zip_read_string(bufp ? &cur : NULL, fp, comment_len, 0, error);
+	zde->comment = _zip_read_string(bufp ? &cur : NULL, src, comment_len, 0, error);
 	if (!zde->comment)
 	    return -1;
 
 	if (zde->bitflags & ZIP_GPBF_ENCODING_UTF_8) {
 	    if (_zip_guess_encoding(zde->comment, ZIP_ENCODING_UTF8_KNOWN) == ZIP_ENCODING_ERROR) {
-		_zip_error_set(error, ZIP_ER_INCONS, 0);
+		zip_error_set(error, ZIP_ER_INCONS, 0);
 		return -1;
 	    }
 	}
@@ -460,29 +470,28 @@
 		+ (zde->disk_number == ZIP_UINT16_MAX) * 4;
 
 	if (got_len != needed_len) {
-	    _zip_error_set(error, ZIP_ER_INCONS, 0);
+	    zip_error_set(error, ZIP_ER_INCONS, 0);
 	    return -1;
 	}
 	
 	if (zde->uncomp_size == ZIP_UINT32_MAX)
-	    zde->uncomp_size = _zip_read8(&ef);
+	    zde->uncomp_size = _zip_get_64(&ef);
 	else if (local)
 	    ef += 8;
 	if (zde->comp_size == ZIP_UINT32_MAX)
-	    zde->comp_size = _zip_read8(&ef);
+	    zde->comp_size = _zip_get_64(&ef);
 	if (!local) {
 	    if (zde->offset == ZIP_UINT32_MAX)
-		zde->offset = _zip_read8(&ef);
+		zde->offset = _zip_get_64(&ef);
 	    if (zde->disk_number == ZIP_UINT16_MAX)
-		zde->disk_number = _zip_read4(&ef);
+		zde->disk_number = _zip_get_32(&ef);
 	}
     }
     
-    if (!local) {
-        if (zde->offset > ZIP_OFF_MAX) {
-            _zip_error_set(error, ZIP_ER_SEEK, EFBIG);
-            return -1;
-        }
+    /* zip_source_seek / zip_source_tell don't support values > ZIP_INT64_MAX */
+    if (zde->offset > ZIP_INT64_MAX) {
+	zip_error_set(error, ZIP_ER_SEEK, EFBIG);
+	return -1;
     }
     
     zde->extra_fields = _zip_ef_remove_internal(zde->extra_fields);
@@ -508,7 +517,7 @@
 	return str;
 
     ef++;
-    ef_crc = _zip_read4(&ef);
+    ef_crc = _zip_get_32(&ef);
 
     if (_zip_string_crc32(str) == ef_crc) {
 	struct zip_string *ef_str = _zip_string_new(ef, ef_len-5, ZIP_FL_ENC_UTF_8, NULL);
@@ -524,7 +533,7 @@
 
 
 zip_int32_t
-_zip_dirent_size(FILE *f, zip_uint16_t flags, struct zip_error *error)
+_zip_dirent_size(zip_source_t *src, zip_uint16_t flags, zip_error_t *error)
 {
     zip_int32_t size;
     int local = (flags & ZIP_EF_LOCAL);
@@ -534,19 +543,18 @@
 
     size = local ? LENTRYSIZE : CDENTRYSIZE;
 
-    if (fseek(f, local ? 26 : 28, SEEK_CUR) < 0) {
-	_zip_error_set(error, ZIP_ER_SEEK, errno);
+    if (zip_source_seek(src, local ? 26 : 28, SEEK_CUR) < 0) {
+        zip_error_set_from_source(error, src);
 	return -1;
     }
 
-    if (fread(b, (local ? 4 : 6), 1, f) != 1) {
-	_zip_error_set(error, ZIP_ER_READ, errno);
+    if (_zip_read(src, b, local ? 4 : 6, error) < 0) {
 	return -1;
     }
 
     p = b;
     for (i=0; i<(local ? 2 : 3); i++) {
-	size += _zip_read2(&p);
+	size += _zip_get_16(&p);
     }
 
     return size;
@@ -606,8 +614,8 @@
 }
 
 
-/* _zip_dirent_write(zde, fp, flags, error):
-   Writes zip directory entry zde to file fp.
+/* _zip_dirent_write
+   Writes zip directory entry.
 
    If flags & ZIP_EF_LOCAL, it writes a local header instead of a central
    directory entry.  If flags & ZIP_EF_FORCE_ZIP64, a ZIP64 extra field is written, even if not needed.
@@ -617,20 +625,23 @@
 */
 
 int
-_zip_dirent_write(struct zip_dirent *de, FILE *fp, zip_flags_t flags, struct zip_error *error)
+_zip_dirent_write(struct zip *za, struct zip_dirent *de, zip_flags_t flags)
 {
-    unsigned short dostime, dosdate;
+    zip_uint16_t dostime, dosdate;
     enum zip_encoding_type com_enc, name_enc;
     struct zip_extra_field *ef;
     zip_uint8_t ef_zip64[24], *ef_zip64_p;
     int is_zip64;
     int is_really_zip64;
+    zip_uint8_t buf[CDENTRYSIZE], *p;
 
     ef = NULL;
 
     is_zip64 = 0;
 
-    fwrite((flags & ZIP_FL_LOCAL) ? LOCAL_MAGIC : CENTRAL_MAGIC, 1, 4, fp);
+    p = buf;
+
+    _zip_put_data(&p, (flags & ZIP_FL_LOCAL) ? LOCAL_MAGIC : CENTRAL_MAGIC, 4);
 
     name_enc = _zip_guess_encoding(de->filename, ZIP_ENCODING_UNKNOWN);
     com_enc = _zip_guess_encoding(de->comment, ZIP_ENCODING_UNKNOWN);
@@ -642,12 +653,12 @@
     else {
 	de->bitflags &= ~ZIP_GPBF_ENCODING_UTF_8;
 	if (name_enc == ZIP_ENCODING_UTF8_KNOWN) {
-	    ef = _zip_ef_utf8(ZIP_EF_UTF_8_NAME, de->filename, error);
+	    ef = _zip_ef_utf8(ZIP_EF_UTF_8_NAME, de->filename, &za->error);
 	    if (ef == NULL)
 		return -1;
 	}
 	if ((flags & ZIP_FL_LOCAL) == 0 && com_enc == ZIP_ENCODING_UTF8_KNOWN){
-	    struct zip_extra_field *ef2 = _zip_ef_utf8(ZIP_EF_UTF_8_COMMENT, de->comment, error);
+	    struct zip_extra_field *ef2 = _zip_ef_utf8(ZIP_EF_UTF_8_COMMENT, de->comment, &za->error);
 	    if (ef2 == NULL) {
 		_zip_ef_free(ef);
 		return -1;
@@ -660,18 +671,18 @@
     ef_zip64_p = ef_zip64;
     if (flags & ZIP_FL_LOCAL) {
 	if ((flags & ZIP_FL_FORCE_ZIP64) || de->comp_size > ZIP_UINT32_MAX || de->uncomp_size > ZIP_UINT32_MAX) {
-	    _zip_poke8(de->uncomp_size, &ef_zip64_p);
-	    _zip_poke8(de->comp_size, &ef_zip64_p);
+	    _zip_put_64(&ef_zip64_p, de->uncomp_size);
+	    _zip_put_64(&ef_zip64_p, de->comp_size);
 	}
     }
     else {
 	if ((flags & ZIP_FL_FORCE_ZIP64) || de->comp_size > ZIP_UINT32_MAX || de->uncomp_size > ZIP_UINT32_MAX || de->offset > ZIP_UINT32_MAX) {
 	    if (de->uncomp_size >= ZIP_UINT32_MAX)
-		_zip_poke8(de->uncomp_size, &ef_zip64_p);
+		_zip_put_64(&ef_zip64_p, de->uncomp_size);
 	    if (de->comp_size >= ZIP_UINT32_MAX)
-		_zip_poke8(de->comp_size, &ef_zip64_p);
+		_zip_put_64(&ef_zip64_p, de->comp_size);
 	    if (de->offset >= ZIP_UINT32_MAX)
-		_zip_poke8(de->offset, &ef_zip64_p);
+		_zip_put_64(&ef_zip64_p, de->offset);
 	}
     }
 
@@ -688,59 +699,70 @@
 	is_really_zip64 = is_zip64;
     
     if ((flags & ZIP_FL_LOCAL) == 0)
-	_zip_write2(is_really_zip64 ? 45 : de->version_madeby, fp);
-    _zip_write2(is_really_zip64 ? 45 : de->version_needed, fp);
-    _zip_write2(de->bitflags&0xfff9, fp); /* clear compression method specific flags */
-    _zip_write2((zip_uint16_t)de->comp_method, fp); /* TODO: can it be ZIP_CM_DEFAULT? */
+	_zip_put_16(&p, is_really_zip64 ? 45 : de->version_madeby);
+    _zip_put_16(&p, is_really_zip64 ? 45 : de->version_needed);
+    _zip_put_16(&p, de->bitflags&0xfff9); /* clear compression method specific flags */
+    _zip_put_16(&p, (zip_uint16_t)de->comp_method);
 
     _zip_u2d_time(de->last_mod, &dostime, &dosdate);
-    _zip_write2(dostime, fp);
-    _zip_write2(dosdate, fp);
+    _zip_put_16(&p, dostime);
+    _zip_put_16(&p, dosdate);
 
-    _zip_write4(de->crc, fp);
+    _zip_put_32(&p, de->crc);
     if (de->comp_size < ZIP_UINT32_MAX)
-	_zip_write4((zip_uint32_t)de->comp_size, fp);
+	_zip_put_32(&p, (zip_uint32_t)de->comp_size);
     else
-	_zip_write4(ZIP_UINT32_MAX, fp);
+	_zip_put_32(&p, ZIP_UINT32_MAX);
     if (de->uncomp_size < ZIP_UINT32_MAX)
-	_zip_write4((zip_uint32_t)de->uncomp_size, fp);
+	_zip_put_32(&p, (zip_uint32_t)de->uncomp_size);
     else
-	_zip_write4(ZIP_UINT32_MAX, fp);
+	_zip_put_32(&p, ZIP_UINT32_MAX);
 
-    _zip_write2(_zip_string_length(de->filename), fp);
-    _zip_write2(_zip_ef_size(de->extra_fields, flags) + _zip_ef_size(ef, ZIP_EF_BOTH), fp);
+    _zip_put_16(&p, _zip_string_length(de->filename));
+    _zip_put_16(&p, _zip_ef_size(de->extra_fields, flags) + _zip_ef_size(ef, ZIP_EF_BOTH));
     
     if ((flags & ZIP_FL_LOCAL) == 0) {
-	_zip_write2(_zip_string_length(de->comment), fp);
-	_zip_write2((zip_uint16_t)de->disk_number, fp);
-	_zip_write2(de->int_attrib, fp);
-	_zip_write4(de->ext_attrib, fp);
+	_zip_put_16(&p, _zip_string_length(de->comment));
+	_zip_put_16(&p, (zip_uint16_t)de->disk_number);
+	_zip_put_16(&p, de->int_attrib);
+	_zip_put_32(&p, de->ext_attrib);
 	if (de->offset < ZIP_UINT32_MAX)
-	    _zip_write4((zip_uint32_t)de->offset, fp);
+	    _zip_put_32(&p, (zip_uint32_t)de->offset);
 	else
-	    _zip_write4(ZIP_UINT32_MAX, fp);
+	    _zip_put_32(&p, ZIP_UINT32_MAX);
     }
 
-    if (de->filename)
-	_zip_string_write(de->filename, fp);
+    if (_zip_write(za, buf, (zip_uint64_t)(p-buf)) < 0) {
+	return -1;
+    }
 
-    if (ef)
-	_zip_ef_write(ef, ZIP_EF_BOTH, fp);
-    if (de->extra_fields)
-	_zip_ef_write(de->extra_fields, flags, fp);
+    if (de->filename) {
+	if (_zip_string_write(za, de->filename) < 0) {
+	    return -1;
+	}
+    }
+
+    if (ef) {
+	if (_zip_ef_write(za, ef, ZIP_EF_BOTH) < 0) {
+	    return -1;
+	}
+    }
+    if (de->extra_fields) {
+	if (_zip_ef_write(za, de->extra_fields, flags) < 0) {
+	    return -1;
+	}
+    }
 
     if ((flags & ZIP_FL_LOCAL) == 0) {
-	if (de->comment)
-	    _zip_string_write(de->comment, fp);
+	if (de->comment) {
+	    if (_zip_string_write(za, de->comment) < 0) {
+		return -1;
+	    }
+	}
     }
 
     _zip_ef_free(ef);
 
-    if (ferror(fp)) {
-	_zip_error_set(error, ZIP_ER_WRITE, errno);
-	return -1;
-    }
-
     return is_zip64;
 }
 
@@ -782,13 +804,13 @@
     }
     
     if ((data=(zip_uint8_t *)malloc(len+5)) == NULL) {
-	_zip_error_set(error, ZIP_ER_MEMORY, 0);
+	zip_error_set(error, ZIP_ER_MEMORY, 0);
 	return NULL;
     }
 
     p = data;
     *(p++) = 1;
-    _zip_poke4(_zip_string_crc32(str), &p);
+    _zip_put_32(&p, _zip_string_crc32(str));
     memcpy(p, raw, len);
     p += len;
 
@@ -805,17 +827,17 @@
 	error = &za->error;
 
     if (idx >= za->nentry) {
-	_zip_error_set(error, ZIP_ER_INVAL, 0);
+	zip_error_set(error, ZIP_ER_INVAL, 0);
 	return NULL;
     }
 
     if ((flags & ZIP_FL_UNCHANGED) || za->entry[idx].changes == NULL) {
 	if (za->entry[idx].orig == NULL) {
-	    _zip_error_set(error, ZIP_ER_INVAL, 0);
+	    zip_error_set(error, ZIP_ER_INVAL, 0);
 	    return NULL;
 	}
 	if (za->entry[idx].deleted && (flags & ZIP_FL_UNCHANGED) == 0) {
-	    _zip_error_set(error, ZIP_ER_DELETED, 0);
+	    zip_error_set(error, ZIP_ER_DELETED, 0);
 	    return NULL;
 	}
 	return za->entry[idx].orig;
@@ -825,161 +847,6 @@
 }
 
 
-zip_uint16_t
-_zip_read2(const zip_uint8_t **a)
-{
-    zip_uint16_t ret;
-
-    ret = (zip_uint16_t)((*a)[0]+((*a)[1]<<8));
-    *a += 2;
-
-    return ret;
-}
-
-
-zip_uint32_t
-_zip_read4(const zip_uint8_t **a)
-{
-    zip_uint32_t ret;
-
-    ret = ((((((zip_uint32_t)(*a)[3]<<8)+(*a)[2])<<8)+(*a)[1])<<8)+(*a)[0];
-    *a += 4;
-
-    return ret;
-}
-
-
-zip_uint64_t
-_zip_read8(const zip_uint8_t **a)
-{
-    zip_uint64_t x, y;
-
-    x = ((((((zip_uint64_t)(*a)[3]<<8)+(*a)[2])<<8)+(*a)[1])<<8)+(*a)[0];
-    *a += 4;
-    y = ((((((zip_uint64_t)(*a)[3]<<8)+(*a)[2])<<8)+(*a)[1])<<8)+(*a)[0];
-    *a += 4;
-
-    return x+(y<<32);
-}
-
-
-zip_uint8_t *
-_zip_read_data(const zip_uint8_t **buf, FILE *fp, size_t len, int nulp, struct zip_error *error)
-{
-    zip_uint8_t *r;
-
-    if (len == 0 && nulp == 0)
-	return NULL;
-
-    r = (zip_uint8_t *)malloc(nulp ? len+1 : len);
-    if (!r) {
-	_zip_error_set(error, ZIP_ER_MEMORY, 0);
-	return NULL;
-    }
-
-    if (buf) {
-	memcpy(r, *buf, len);
-	*buf += len;
-    }
-    else {
-	if (fread(r, 1, len, fp)<len) {
-	    free(r);
-            if (ferror(fp))
-                _zip_error_set(error, ZIP_ER_READ, errno);
-            else
-                _zip_error_set(error, ZIP_ER_INCONS, 0);
-	    return NULL;
-	}
-    }
-
-    if (nulp) {
-	zip_uint8_t *o;
-	/* replace any in-string NUL characters with spaces */
-	r[len] = 0;
-	for (o=r; o<r+len; o++)
-	    if (*o == '\0')
-		*o = ' ';
-    }
-
-    return r;
-}
-
-
-static struct zip_string *
-_zip_read_string(const zip_uint8_t **buf, FILE *fp, zip_uint16_t len, int nulp, struct zip_error *error)
-{
-    zip_uint8_t *raw;
-    struct zip_string *s;
-
-    if ((raw=_zip_read_data(buf, fp, len, nulp, error)) == NULL)
-	return NULL;
-
-    s = _zip_string_new(raw, len, ZIP_FL_ENC_GUESS, error);
-    free(raw);
-    return s;
-}
-
-
-void
-_zip_poke4(zip_uint32_t i, zip_uint8_t **p)
-{
-    *((*p)++) = i&0xff;
-    *((*p)++) = (i>>8)&0xff;
-    *((*p)++) = (i>>16)&0xff;
-    *((*p)++) = (i>>24)&0xff;
-}
-
-
-void
-_zip_poke8(zip_uint64_t i, zip_uint8_t **p)
-{
-    *((*p)++) = i&0xff;
-    *((*p)++) = (i>>8)&0xff;
-    *((*p)++) = (i>>16)&0xff;
-    *((*p)++) = (i>>24)&0xff;
-    *((*p)++) = (i>>32)&0xff;
-    *((*p)++) = (i>>40)&0xff;
-    *((*p)++) = (i>>48)&0xff;
-    *((*p)++) = (i>>56)&0xff;
-}
-
-
-void
-_zip_write2(zip_uint16_t i, FILE *fp)
-{
-    putc(i&0xff, fp);
-    putc((i>>8)&0xff, fp);
-
-    return;
-}
-
-
-void
-_zip_write4(zip_uint32_t i, FILE *fp)
-{
-    putc(i&0xff, fp);
-    putc((i>>8)&0xff, fp);
-    putc((i>>16)&0xff, fp);
-    putc((i>>24)&0xff, fp);
-    
-    return;
-}
-
-
-void
-_zip_write8(zip_uint64_t i, FILE *fp)
-{
-    putc(i&0xff, fp);
-    putc((i>>8)&0xff, fp);
-    putc((i>>16)&0xff, fp);
-    putc((i>>24)&0xff, fp);
-    putc((i>>32)&0xff, fp);
-    putc((i>>40)&0xff, fp);
-    putc((i>>48)&0xff, fp);
-    putc((i>>56)&0xff, fp);
-    
-    return;
-}
 
 
 void
diff --git a/lib/zip_discard.c b/lib/zip_discard.c
index 3fc6d8f..2ca11df 100644
--- a/lib/zip_discard.c
+++ b/lib/zip_discard.c
@@ -49,11 +49,10 @@
     if (za == NULL)
 	return;
 
-    if (za->zn)
-	free(za->zn);
-
-    if (za->zp)
-	fclose(za->zp);
+    if (za->src) {
+	zip_source_close(za->src);
+	zip_source_free(za->src);
+    }
 
     free(za->default_password);
     _zip_string_free(za->comment_orig);
@@ -65,12 +64,12 @@
 	free(za->entry);
     }
 
-    for (i=0; i<za->nsource; i++) {
-	_zip_source_filep_invalidate(za->source[i]);
+    for (i=0; i<za->nopen_source; i++) {
+	_zip_source_invalidate(za->open_source[i]);
     }
+    free(za->open_source);
 
-    _zip_error_fini(&za->error);
-    free(za->source);
+    zip_error_fini(&za->error);
     
     free(za);
 
diff --git a/lib/zip_entry.c b/lib/zip_entry.c
index acce311..73a1f50 100644
--- a/lib/zip_entry.c
+++ b/lib/zip_entry.c
@@ -1,6 +1,6 @@
 /*
   zip_entry.c -- struct zip_entry helper functions
-  Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
diff --git a/lib/zip_err_str.c b/lib/zip_err_str.c
index 830eb1c..65698be 100644
--- a/lib/zip_err_str.c
+++ b/lib/zip_err_str.c
@@ -23,7 +23,7 @@
     "Malloc failure",
     "Entry has been changed",
     "Compression method not supported",
-    "Premature EOF",
+    "Premature end of file",
     "Invalid argument",
     "Not a zip archive",
     "Internal error",
@@ -34,6 +34,9 @@
     "Read-only archive", 
     "No password provided",
     "Wrong password provided",
+    "Operation not supported",
+    "Resource still in use",
+    "Tell error",
 };
 
 const int _zip_nerr_str = sizeof(_zip_err_str)/sizeof(_zip_err_str[0]);
@@ -71,4 +74,7 @@
     N, 
     N,
     N,
+    N,
+    N,
+    S,
 };
diff --git a/lib/zip_error.c b/lib/zip_error.c
index ca95c2f..6ccf397 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-2013 Dieter Baron and Thomas Klausner
+  zip_error.c -- zip_error_t helper functions
+  Copyright (C) 1999-2014 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>
@@ -37,8 +37,46 @@
 #include "zipint.h"
 
 
+ZIP_EXTERN int
+zip_error_code_system(const zip_error_t *error) {
+    return error->sys_err;
+}
+
+
+ZIP_EXTERN int
+zip_error_code_zip(const zip_error_t *error) {
+    return error->zip_err;
+}
+
+
+ZIP_EXTERN void
+zip_error_fini(zip_error_t *err)
+{
+    free(err->str);
+    err->str = NULL;
+}
+
+
+ZIP_EXTERN void
+zip_error_init(zip_error_t *err)
+{
+    err->zip_err = ZIP_ER_OK;
+    err->sys_err = 0;
+    err->str = NULL;
+}
+
+
+ZIP_EXTERN int
+zip_error_system_type(const zip_error_t *error) {
+    if (error->zip_err < 0 || error->zip_err >= _zip_nerr_str)
+        return ZIP_ET_NONE;
+    
+    return _zip_err_type[error->zip_err];
+}
+
+
 void
-_zip_error_clear(struct zip_error *err)
+_zip_error_clear(zip_error_t *err)
 {
     if (err == NULL)
 	return;
@@ -49,7 +87,7 @@
 
 
 void
-_zip_error_copy(struct zip_error *dst, const struct zip_error *src)
+_zip_error_copy(zip_error_t *dst, const zip_error_t *src)
 {
     dst->zip_err = src->zip_err;
     dst->sys_err = src->sys_err;
@@ -57,20 +95,12 @@
 
 
 void
-_zip_error_fini(struct zip_error *err)
-{
-    free(err->str);
-    err->str = NULL;
-}
-
-
-void
-_zip_error_get(const struct zip_error *err, int *zep, int *sep)
+_zip_error_get(const zip_error_t *err, int *zep, int *sep)
 {
     if (zep)
 	*zep = err->zip_err;
     if (sep) {
-	if (zip_error_get_sys_type(err->zip_err) != ZIP_ET_NONE)
+	if (zip_error_system_type(err) != ZIP_ET_NONE)
 	    *sep = err->sys_err;
 	else
 	    *sep = 0;
@@ -79,16 +109,7 @@
 
 
 void
-_zip_error_init(struct zip_error *err)
-{
-    err->zip_err = ZIP_ER_OK;
-    err->sys_err = 0;
-    err->str = NULL;
-}
-
-
-void
-_zip_error_set(struct zip_error *err, int ze, int se)
+zip_error_set(zip_error_t *err, int ze, int se)
 {
     if (err) {
 	err->zip_err = ze;
@@ -98,10 +119,22 @@
 
 
 void
-_zip_error_set_from_source(struct zip_error *err, struct zip_source *src)
+zip_error_set_from_source(zip_error_t *err, struct zip_source *src)
 {
-    int ze, se;
+    _zip_error_copy(err, zip_source_error(src));
+}
+
+
+zip_int64_t
+zip_error_to_data(const zip_error_t *error, void *data, zip_uint64_t length)
+{
+    int *e = (int *)data;
     
-    zip_source_error(src, &ze, &se);
-    _zip_error_set(err, ze, se);
+    if (length < sizeof(int)*2) {
+        return -1;
+    }
+    
+    e[0] = zip_error_code_zip(error);
+    e[1] = zip_error_code_system(error);
+    return sizeof(int)*2;
 }
diff --git a/lib/zip_error_clear.c b/lib/zip_error_clear.c
index a0ceda5..3e6e482 100644
--- a/lib/zip_error_clear.c
+++ b/lib/zip_error_clear.c
@@ -1,6 +1,6 @@
 /*
   zip_error_clear.c -- clear zip error
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
diff --git a/lib/zip_error_get.c b/lib/zip_error_get.c
index 80fd2ba..ef98b0a 100644
--- a/lib/zip_error_get.c
+++ b/lib/zip_error_get.c
@@ -1,6 +1,6 @@
 /*
   zip_error_get.c -- get zip error
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -32,6 +32,7 @@
 */
 
 
+#define _ZIP_COMPILING_DEPRECATED
 #include "zipint.h"
 
 
diff --git a/lib/zip_error_get_sys_type.c b/lib/zip_error_get_sys_type.c
index e96a676..7e27bbf 100644
--- a/lib/zip_error_get_sys_type.c
+++ b/lib/zip_error_get_sys_type.c
@@ -1,6 +1,6 @@
 /*
   zip_error_get_sys_type.c -- return type of system error code
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -31,7 +31,7 @@
   IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
-
+#define _ZIP_COMPILING_DEPRECATED
 #include "zipint.h"
 
 
diff --git a/lib/zip_error_strerror.c b/lib/zip_error_strerror.c
index dd871d3..a4e603d 100644
--- a/lib/zip_error_strerror.c
+++ b/lib/zip_error_strerror.c
@@ -1,6 +1,6 @@
 /*
   zip_error_sterror.c -- get string representation of struct zip_error
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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,13 +40,13 @@
 #include "zipint.h"
 
 
-const char *
-_zip_error_strerror(struct zip_error *err)
+ZIP_EXTERN const char *
+zip_error_strerror(struct zip_error *err)
 {
     const char *zs, *ss;
     char buf[128], *s;
 
-    _zip_error_fini(err);
+    zip_error_fini(err);
 
     if (err->zip_err < 0 || err->zip_err >= _zip_nerr_str) {
 	sprintf(buf, "Unknown error %d", err->zip_err);
diff --git a/lib/zip_error_to_str.c b/lib/zip_error_to_str.c
index 8d2b4fd..77af776 100644
--- a/lib/zip_error_to_str.c
+++ b/lib/zip_error_to_str.c
@@ -1,6 +1,6 @@
 /*
   zip_error_to_str.c -- get string representation of zip error code
-  Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
diff --git a/lib/zip_extra_field.c b/lib/zip_extra_field.c
index 6ec2db3..4095613 100644
--- a/lib/zip_extra_field.c
+++ b/lib/zip_extra_field.c
@@ -1,6 +1,6 @@
 /*
   zip_extra_field.c -- manipulate extra fields
-  Copyright (C) 2012-2013 Dieter Baron and Thomas Klausner
+  Copyright (C) 2012-2014 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>
@@ -48,7 +48,7 @@
     
     while (ef) {
         if ((def=_zip_ef_new(ef->id, ef->size, ef->data, ef->flags)) == NULL) {
-            _zip_error_set(error, ZIP_ER_MEMORY, 0);
+            zip_error_set(error, ZIP_ER_MEMORY, 0);
             _zip_ef_free(head);
             return NULL;
         }
@@ -142,7 +142,7 @@
 	}
     }
 
-    _zip_error_set(error, ZIP_ER_NOENT, 0);
+    zip_error_set(error, ZIP_ER_NOENT, 0);
     return NULL;
 }
 
@@ -217,22 +217,22 @@
     ef_head = ef = NULL;
     for (p=data; p<data+len; p+=flen) {
 	if (p+4 > data+len) {
-	    _zip_error_set(error, ZIP_ER_INCONS, 0);
+	    zip_error_set(error, ZIP_ER_INCONS, 0);
 	    _zip_ef_free(ef_head);
 	    return NULL;
 	}
 
-	fid = _zip_read2(&p);
-	flen = _zip_read2(&p);
+	fid = _zip_get_16(&p);
+	flen = _zip_get_16(&p);
 
 	if (p+flen > data+len) {
-	    _zip_error_set(error, ZIP_ER_INCONS, 0);
+	    zip_error_set(error, ZIP_ER_INCONS, 0);
 	    _zip_ef_free(ef_head);
 	    return NULL;
 	}
 
 	if ((ef2=_zip_ef_new(fid, flen, p, flags)) == NULL) {
-	    _zip_error_set(error, ZIP_ER_MEMORY, 0);
+	    zip_error_set(error, ZIP_ER_MEMORY, 0);
 	    _zip_ef_free(ef_head);
 	    return NULL;
 	}
@@ -294,17 +294,27 @@
 }
 
 
-void
-_zip_ef_write(const struct zip_extra_field *ef, zip_flags_t flags, FILE *f)
+int
+_zip_ef_write(struct zip *za, const struct zip_extra_field *ef, zip_flags_t flags)
 {
+    zip_uint8_t b[4], *p;
+
     for (; ef; ef=ef->next) {
 	if (ef->flags & flags & ZIP_EF_BOTH) {
-	    _zip_write2(ef->id, f);
-	    _zip_write2(ef->size, f);
-	    if (ef->size > 0)
-		fwrite(ef->data, ef->size, 1, f);
+	    p = b;
+	    _zip_put_16(&p, ef->id);
+	    _zip_put_16(&p, ef->size);
+	    if (_zip_write(za, b, 4) < 0) {
+		return -1;
+	    }
+	    if (ef->size > 0) {
+		if (_zip_write(za, ef->data, ef->size) < 0) {
+		    return -1;
+		}
+	    }
 	}
     }
+    return 0;
 }
 
 
@@ -317,7 +327,7 @@
     zip_uint16_t fname_len, ef_len;
 
     if (idx >= za->nentry) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
@@ -326,31 +336,34 @@
     if (e->orig == NULL || e->orig->local_extra_fields_read)
 	return 0;
 
+    if (e->orig->offset + 26 > ZIP_INT64_MAX) {
+	zip_error_set(&za->error, ZIP_ER_SEEK, EFBIG);
+	return -1;
+    }	
 
-    if (fseeko(za->zp, (off_t)(e->orig->offset + 26), SEEK_SET) < 0) {
-	_zip_error_set(&za->error, ZIP_ER_SEEK, errno);
+    if (zip_source_seek(za->src, (zip_int64_t)(e->orig->offset + 26), SEEK_SET) < 0) {
+	zip_error_set_from_source(&za->error, za->src);
 	return -1;
     }
 
-    if (fread(b, sizeof(b), 1, za->zp) != 1) {
-	_zip_error_set(&za->error, ZIP_ER_READ, errno);
+    if (_zip_read(za->src, b, sizeof(b), &za->error) < 0) {
 	return -1;
     }
 
     p = b;
-    fname_len = _zip_read2(&p);
-    ef_len = _zip_read2(&p);
+    fname_len = _zip_get_16(&p);
+    ef_len = _zip_get_16(&p);
 
     if (ef_len > 0) {
 	struct zip_extra_field *ef;
 	zip_uint8_t *ef_raw;
 
-	if (fseek(za->zp, fname_len, SEEK_CUR) < 0) {
-	    _zip_error_set(&za->error, ZIP_ER_SEEK, errno);
+	if (zip_source_seek(za->src, fname_len, SEEK_CUR) < 0) {
+	    zip_error_set(&za->error, ZIP_ER_SEEK, errno);
 	    return -1;
 	}
 
-	ef_raw = _zip_read_data(NULL, za->zp, ef_len, 0, &za->error);
+	ef_raw = _zip_read_data(NULL, za->src, ef_len, 0, &za->error);
 
 	if (ef_raw == NULL)
 	    return -1;
diff --git a/lib/zip_extra_field_api.c b/lib/zip_extra_field_api.c
index 357674b..f567c28 100644
--- a/lib/zip_extra_field_api.c
+++ b/lib/zip_extra_field_api.c
@@ -1,6 +1,6 @@
 /*
   zip_extra_field_api.c -- public extra fields API functions
-  Copyright (C) 2012-2013 Dieter Baron and Thomas Klausner
+  Copyright (C) 2012-2014 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>
@@ -41,12 +41,12 @@
     struct zip_dirent *de;
 
     if ((flags & ZIP_EF_BOTH) == 0) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
     if (((flags & ZIP_EF_BOTH) == ZIP_EF_BOTH) && (ef_idx != ZIP_EXTRA_FIELD_ALL)) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
     
@@ -54,7 +54,7 @@
 	return -1;
     
     if (ZIP_IS_RDONLY(za)) {
-	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
 	return -1;
     }
 
@@ -74,12 +74,12 @@
     struct zip_dirent *de;
 
     if ((flags & ZIP_EF_BOTH) == 0) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
     if (((flags & ZIP_EF_BOTH) == ZIP_EF_BOTH) && (ef_idx != ZIP_EXTRA_FIELD_ALL)) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
     
@@ -87,7 +87,7 @@
 	return -1;
 
     if (ZIP_IS_RDONLY(za)) {
-	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
 	return -1;
     }
     
@@ -111,7 +111,7 @@
     int i;
 
     if ((flags & ZIP_EF_BOTH) == 0) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return NULL;
     }
 
@@ -141,7 +141,7 @@
 	}
     }
 
-    _zip_error_set(&za->error, ZIP_ER_NOENT, 0);
+    zip_error_set(&za->error, ZIP_ER_NOENT, 0);
     return NULL;
 
 }
@@ -153,7 +153,7 @@
     struct zip_dirent *de;
 
     if ((flags & ZIP_EF_BOTH) == 0) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return NULL;
     }
 
@@ -176,7 +176,7 @@
     zip_uint16_t n;
 
     if ((flags & ZIP_EF_BOTH) == 0) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
@@ -204,7 +204,7 @@
     zip_uint16_t n;
 
     if ((flags & ZIP_EF_BOTH) == 0) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
@@ -233,7 +233,7 @@
     int i, found, new_len;
 
     if ((flags & ZIP_EF_BOTH) == 0) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
@@ -241,12 +241,12 @@
 	return -1;
     
     if (ZIP_IS_RDONLY(za)) {
-	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
 	return -1;
     }
     
     if (ZIP_EF_IS_INTERNAL(ef_id)) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
@@ -272,7 +272,7 @@
     }
 
     if (i < ef_idx && ef_idx != ZIP_EXTRA_FIELD_NEW) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
@@ -291,12 +291,12 @@
     new_len += len + 4;
 
     if (new_len > ZIP_UINT16_MAX) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
     
     if ((ef_new=_zip_ef_new(ef_id, len, data, flags)) == NULL) {
-	_zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+	zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
 	return -1;
     }
 
@@ -334,7 +334,7 @@
     struct zip_entry *e;
     
     if (idx >= za->nentry) {
-        _zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+        zip_error_set(&za->error, ZIP_ER_INVAL, 0);
         return -1;
     }
     
@@ -350,7 +350,7 @@
     
     if (e->changes == NULL) {
         if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) {
-            _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+            zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
             return -1;
         }
     }
diff --git a/lib/zip_fclose.c b/lib/zip_fclose.c
index 00265cd..d5148c7 100644
--- a/lib/zip_fclose.c
+++ b/lib/zip_fclose.c
@@ -49,7 +49,7 @@
     if (zf->error.zip_err)
 	ret = zf->error.zip_err;
 
-    _zip_error_fini(&zf->error);
+    zip_error_fini(&zf->error);
     free(zf);
     return ret;
 }
diff --git a/lib/zip_fdopen.c b/lib/zip_fdopen.c
index eb4d7ec..ac11316 100644
--- a/lib/zip_fdopen.c
+++ b/lib/zip_fdopen.c
@@ -1,6 +1,6 @@
 /*
   zip_fdopen.c -- open read-only archive from file descriptor
-  Copyright (C) 2009-2010 Dieter Baron and Thomas Klausner
+  Copyright (C) 2009-2014 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,34 +40,43 @@
 {
     int fd;
     FILE *fp;
-    unsigned int flags;
+    struct zip *za;
+    struct zip_source *src;
+    struct zip_error error;
 
-    if (_flags < 0) {
-        if (zep)
-            *zep = ZIP_ER_INVAL;
+    if (_flags < 0 || (_flags & ZIP_TRUNCATE)) {
+	_zip_set_open_error(zep, NULL, ZIP_ER_INVAL);
         return  NULL;
     }
-    flags = (unsigned int)_flags;
         
-    if (flags & ZIP_TRUNCATE) {
-	*zep = ZIP_ER_INVAL;
-	return NULL;
-    }
-
     /* We dup() here to avoid messing with the passed in fd.
        We could not restore it to the original state in case of error. */
 
     if ((fd=dup(fd_orig)) < 0) {
-	*zep = ZIP_ER_OPEN;
+	_zip_set_open_error(zep, NULL, ZIP_ER_OPEN);
 	return NULL;
     }
 
     if ((fp=fdopen(fd, "rb")) == NULL) {
 	close(fd);
-	*zep = ZIP_ER_OPEN;
+	_zip_set_open_error(zep, NULL, ZIP_ER_OPEN);
 	return NULL;
     }
 
+    zip_error_init(&error);
+    if ((src = zip_source_filep_create(fp, 0, -1, &error)) == NULL) {
+	_zip_set_open_error(zep, &error, 0);
+	zip_error_fini(&error);
+	return NULL;
+    }
+
+    if ((za = zip_open_from_source(src, _flags, &error)) == NULL) {
+	_zip_set_open_error(zep, &error, 0);
+	zip_error_fini(&error);
+	return NULL;
+    }
+
+    zip_error_fini(&error);
     close(fd_orig);
-    return _zip_open(NULL, fp, flags, zep);
+    return za;
 }
diff --git a/lib/zip_file_add.c b/lib/zip_file_add.c
index 35e2e6f..fe95287 100644
--- a/lib/zip_file_add.c
+++ b/lib/zip_file_add.c
@@ -1,6 +1,6 @@
 /*
   zip_file_add.c -- add file via callback function
-  Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -45,7 +45,7 @@
 zip_file_add(struct zip *za, const char *name, struct zip_source *source, zip_flags_t flags)
 {
     if (name == NULL || source == NULL) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 	
diff --git a/lib/zip_file_error_clear.c b/lib/zip_file_error_clear.c
index d4fc1db..927bad9 100644
--- a/lib/zip_file_error_clear.c
+++ b/lib/zip_file_error_clear.c
@@ -1,6 +1,6 @@
 /*
   zip_file_error_clear.c -- clear zip file error
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
diff --git a/lib/zip_file_error_get.c b/lib/zip_file_error_get.c
index c2f8145..1ef56bc 100644
--- a/lib/zip_file_error_get.c
+++ b/lib/zip_file_error_get.c
@@ -1,6 +1,6 @@
 /*
   zip_file_error_get.c -- get zip file error
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -31,7 +31,7 @@
   IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
-
+#define _ZIP_COMPILING_DEPRECATED
 #include "zipint.h"
 
 
diff --git a/lib/zip_file_get_comment.c b/lib/zip_file_get_comment.c
index fc48b1d..4473aa0 100644
--- a/lib/zip_file_get_comment.c
+++ b/lib/zip_file_get_comment.c
@@ -1,6 +1,6 @@
 /*
   zip_file_get_comment.c -- get file comment
-  Copyright (C) 2006-2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 2006-2014 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>
diff --git a/lib/zip_file_get_external_attributes.c b/lib/zip_file_get_external_attributes.c
index 6d09889..bf8bf02 100644
--- a/lib/zip_file_get_external_attributes.c
+++ b/lib/zip_file_get_external_attributes.c
@@ -1,6 +1,6 @@
 /*
   zip_file_get_external_attributes.c -- get opsys/external attributes
-  Copyright (C) 2013 Dieter Baron and Thomas Klausner
+  Copyright (C) 2013-2014 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>
diff --git a/lib/zip_file_get_offset.c b/lib/zip_file_get_offset.c
index 38c456b..feb5c6e 100644
--- a/lib/zip_file_get_offset.c
+++ b/lib/zip_file_get_offset.c
@@ -1,6 +1,6 @@
 /*
   zip_file_get_offset.c -- get offset of file data in archive.
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -56,17 +56,17 @@
 
     offset = za->entry[idx].orig->offset;
 
-    if (fseeko(za->zp, (off_t)offset, SEEK_SET) != 0) {
-	_zip_error_set(error, ZIP_ER_SEEK, errno);
+    if (zip_source_seek(za->src, (zip_int64_t)offset, SEEK_SET) < 0) {
+	zip_error_set_from_source(error, za->src);
 	return 0;
     }
 
     /* TODO: cache? */
-    if ((size=_zip_dirent_size(za->zp, ZIP_EF_LOCAL, error)) < 0)
+    if ((size=_zip_dirent_size(za->src, ZIP_EF_LOCAL, error)) < 0)
 	return 0;
 
-    if (offset+(zip_uint32_t)size > ZIP_OFF_MAX) {
-        _zip_error_set(error, ZIP_ER_SEEK, EFBIG);
+    if (offset+(zip_uint32_t)size > ZIP_INT64_MAX) {
+        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
         return 0;
     }
     
diff --git a/lib/zip_file_rename.c b/lib/zip_file_rename.c
index 64c1642..17d8ff8 100644
--- a/lib/zip_file_rename.c
+++ b/lib/zip_file_rename.c
@@ -1,6 +1,6 @@
 /*
   zip_file_rename.c -- rename file in zip archive
-  Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -44,12 +44,12 @@
     int old_is_dir, new_is_dir;
     
     if (idx >= za->nentry || (name != NULL && strlen(name) > ZIP_UINT16_MAX)) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
     if (ZIP_IS_RDONLY(za)) {
-	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
 	return -1;
     }
 
@@ -60,7 +60,7 @@
     old_is_dir = (old_name[strlen(old_name)-1] == '/');
 
     if (new_is_dir != old_is_dir) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
diff --git a/lib/zip_file_replace.c b/lib/zip_file_replace.c
index 24cf3f0..01a0916 100644
--- a/lib/zip_file_replace.c
+++ b/lib/zip_file_replace.c
@@ -1,6 +1,6 @@
 /*
   zip_file_replace.c -- replace file via callback function
-  Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -39,7 +39,7 @@
 zip_file_replace(struct zip *za, zip_uint64_t idx, struct zip_source *source, zip_flags_t flags)
 {
     if (idx >= za->nentry || source == NULL) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
@@ -59,7 +59,7 @@
     zip_uint64_t za_nentry_prev;
     
     if (ZIP_IS_RDONLY(za)) {
-	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
 	return -1;
     }
 
@@ -93,7 +93,7 @@
     if (za->entry[idx].orig != NULL && (za->entry[idx].changes == NULL || (za->entry[idx].changes->changed & ZIP_DIRENT_COMP_METHOD) == 0)) {
         if (za->entry[idx].changes == NULL) {
             if ((za->entry[idx].changes=_zip_dirent_clone(za->entry[idx].orig)) == NULL) {
-                _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+                zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
                 return -1;
             }
         }
diff --git a/lib/zip_file_set_comment.c b/lib/zip_file_set_comment.c
index 6947818..cdc3a35 100644
--- a/lib/zip_file_set_comment.c
+++ b/lib/zip_file_set_comment.c
@@ -1,6 +1,6 @@
 /*
   zip_file_set_comment.c -- set comment for file in archive
-  Copyright (C) 2006-2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 2006-2014 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>
@@ -49,12 +49,12 @@
 	return -1;
 
     if (ZIP_IS_RDONLY(za)) {
-	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
 	return -1;
     }
 
     if (len > 0 && comment == NULL) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
@@ -83,7 +83,7 @@
     if (changed) {
         if (e->changes == NULL) {
             if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) {
-                _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+                zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
 		_zip_string_free(cstr);
                 return -1;
             }
diff --git a/lib/zip_file_set_external_attributes.c b/lib/zip_file_set_external_attributes.c
index 0ae15b2..61623d2 100644
--- a/lib/zip_file_set_external_attributes.c
+++ b/lib/zip_file_set_external_attributes.c
@@ -1,6 +1,6 @@
 /*
   zip_file_set_external_attributes.c -- set external attributes for entry
-  Copyright (C) 2013 Dieter Baron and Thomas Klausner
+  Copyright (C) 2013-2014 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>
@@ -45,7 +45,7 @@
 	return -1;
 
     if (ZIP_IS_RDONLY(za)) {
-	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
 	return -1;
     }
 
@@ -59,7 +59,7 @@
     if (changed) {
         if (e->changes == NULL) {
             if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) {
-                _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+                zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
                 return -1;
             }
         }
diff --git a/lib/zip_file_set_mtime.c b/lib/zip_file_set_mtime.c
index f5ea0e8..59c3aff 100644
--- a/lib/zip_file_set_mtime.c
+++ b/lib/zip_file_set_mtime.c
@@ -42,7 +42,7 @@
         return -1;
     
     if (ZIP_IS_RDONLY(za)) {
-        _zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+        zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
         return -1;
     }
     
@@ -53,7 +53,7 @@
     if (changed) {
         if (e->changes == NULL) {
             if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) {
-                _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+                zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
                 return -1;
             }
         }
diff --git a/lib/zip_file_strerror.c b/lib/zip_file_strerror.c
index 834435c..95c5923 100644
--- a/lib/zip_file_strerror.c
+++ b/lib/zip_file_strerror.c
@@ -1,6 +1,6 @@
 /*
   zip_file_sterror.c -- get string representation of zip file error
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -38,5 +38,5 @@
 ZIP_EXTERN const char *
 zip_file_strerror(struct zip_file *zf)
 {
-    return _zip_error_strerror(&zf->error);
+    return zip_error_strerror(&zf->error);
 }
diff --git a/lib/zip_filerange_crc.c b/lib/zip_filerange_crc.c
index 5c7ebef..5702aa8 100644
--- a/lib/zip_filerange_crc.c
+++ b/lib/zip_filerange_crc.c
@@ -1,6 +1,6 @@
 /*
   zip_filerange_crc.c -- compute CRC32 for a range of a file
-  Copyright (C) 2008-2013 Dieter Baron and Thomas Klausner
+  Copyright (C) 2008-2014 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,29 +40,37 @@
 
 
 int
-_zip_filerange_crc(FILE *fp, off_t start, off_t len, uLong *crcp,
-		   struct zip_error *errp)
+_zip_filerange_crc(zip_source_t *src, zip_uint64_t start, zip_uint64_t len, uLong *crcp, zip_error_t *error)
 {
     Bytef buf[BUFSIZE];
-    size_t n;
+    zip_int64_t n;
 
     *crcp = crc32(0L, Z_NULL, 0);
 
-    if (fseeko(fp, start, SEEK_SET) != 0) {
-	_zip_error_set(errp, ZIP_ER_SEEK, errno);
+    if (start > ZIP_INT64_MAX) {
+	zip_error_set(error, ZIP_ER_SEEK, EFBIG);
+	return -1;
+    }
+
+    if (zip_source_seek(src, (zip_int64_t)start, SEEK_SET) != 0) {
+	zip_error_set_from_source(error, src);
 	return -1;
     }
     
     while (len > 0) {
-	n = len > BUFSIZE ? BUFSIZE : (size_t)len;
-	if ((n=fread(buf, 1, n, fp)) == 0) {
-	    _zip_error_set(errp, ZIP_ER_READ, errno);
+	n = (zip_int64_t)(len > BUFSIZE ? BUFSIZE : len);
+	if ((n = zip_source_read(src, buf, (zip_uint64_t)n)) < 0) {
+	    zip_error_set_from_source(error, src);
+	    return -1;
+	}
+	if (n == 0) {
+	    zip_error_set(error, ZIP_ER_EOF, 0);
 	    return -1;
 	}
 
 	*crcp = crc32(*crcp, buf, (uInt)n);
 
-	len-= n;
+	len -= (zip_uint64_t)n;
     }
 
     return 0;
diff --git a/lib/zip_fopen.c b/lib/zip_fopen.c
index 2656f8e..ad7c416 100644
--- a/lib/zip_fopen.c
+++ b/lib/zip_fopen.c
@@ -1,6 +1,6 @@
 /*
   zip_fopen.c -- open file in zip archive for reading
-  Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
diff --git a/lib/zip_fopen_encrypted.c b/lib/zip_fopen_encrypted.c
index 3bbb3b6..8b2d05e 100644
--- a/lib/zip_fopen_encrypted.c
+++ b/lib/zip_fopen_encrypted.c
@@ -1,6 +1,6 @@
 /*
   zip_fopen_encrypted.c -- open file for reading with password
-  Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
diff --git a/lib/zip_fopen_index.c b/lib/zip_fopen_index.c
index fa8feff..7887eef 100644
--- a/lib/zip_fopen_index.c
+++ b/lib/zip_fopen_index.c
@@ -1,6 +1,6 @@
 /*
   zip_fopen_index.c -- open file in zip archive for reading by index
-  Copyright (C) 1999-2013 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
diff --git a/lib/zip_fopen_index_encrypted.c b/lib/zip_fopen_index_encrypted.c
index a640610..70b035f 100644
--- a/lib/zip_fopen_index_encrypted.c
+++ b/lib/zip_fopen_index_encrypted.c
@@ -52,7 +52,7 @@
 	return NULL;
 
     if (zip_source_open(src) < 0) {
-	_zip_error_set_from_source(&za->error, src);
+	zip_error_set_from_source(&za->error, src);
 	zip_source_free(src);
 	return NULL;
     }
@@ -74,12 +74,12 @@
     struct zip_file *zf;
 
     if ((zf=(struct zip_file *)malloc(sizeof(struct zip_file))) == NULL) {
-	_zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+	zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
 	return NULL;
     }
 
     zf->za = za;
-    _zip_error_init(&zf->error);
+    zip_error_init(&zf->error);
     zf->eof = 0;
     zf->src = NULL;
 
diff --git a/lib/zip_fread.c b/lib/zip_fread.c
index a226a14..88af7f3 100644
--- a/lib/zip_fread.c
+++ b/lib/zip_fread.c
@@ -1,6 +1,6 @@
 /*
   zip_fread.c -- read from file
-  Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -47,7 +47,7 @@
 	return -1;
 
     if (toread > ZIP_INT64_MAX) {
-	_zip_error_set(&zf->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&zf->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
@@ -55,7 +55,7 @@
 	return 0;
 
     if ((n=zip_source_read(zf->src, outbuf, toread)) < 0) {
-	_zip_error_set_from_source(&zf->error, zf->src);
+	zip_error_set_from_source(&zf->error, zf->src);
 	return -1;
     }
 
diff --git a/lib/zip_get_archive_comment.c b/lib/zip_get_archive_comment.c
index f419342..971883f 100644
--- a/lib/zip_get_archive_comment.c
+++ b/lib/zip_get_archive_comment.c
@@ -1,6 +1,6 @@
 /*
   zip_get_archive_comment.c -- get archive comment
-  Copyright (C) 2006-2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 2006-2014 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>
diff --git a/lib/zip_get_archive_flag.c b/lib/zip_get_archive_flag.c
index 330ba80..6e87126 100644
--- a/lib/zip_get_archive_flag.c
+++ b/lib/zip_get_archive_flag.c
@@ -1,6 +1,6 @@
 /*
   zip_get_archive_flag.c -- get archive global flag
-  Copyright (C) 2008 Dieter Baron and Thomas Klausner
+  Copyright (C) 2008-2014 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>
diff --git a/lib/zip_get_compression_implementation.c b/lib/zip_get_compression_implementation.c
index 8a2cd4d..c1120d3 100644
--- a/lib/zip_get_compression_implementation.c
+++ b/lib/zip_get_compression_implementation.c
@@ -1,6 +1,6 @@
 /*
   zip_get_compression_implementation.c -- get compression implementation
-  Copyright (C) 2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 2009-2014 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>
diff --git a/lib/zip_get_encryption_implementation.c b/lib/zip_get_encryption_implementation.c
index cbcb644..e2f833b 100644
--- a/lib/zip_get_encryption_implementation.c
+++ b/lib/zip_get_encryption_implementation.c
@@ -1,6 +1,6 @@
 /*
   zip_get_encryption_implementation.c -- get encryption implementation
-  Copyright (C) 2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 2009-2014 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>
diff --git a/lib/zip_get_file_comment.c b/lib/zip_get_file_comment.c
index bd59a0f..20bbf99 100644
--- a/lib/zip_get_file_comment.c
+++ b/lib/zip_get_file_comment.c
@@ -1,6 +1,6 @@
 /*
   zip_get_file_comment.c -- get file comment
-  Copyright (C) 2006-2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 2006-2014 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>
diff --git a/lib/zip_get_name.c b/lib/zip_get_name.c
index 205b326..0ae78de 100644
--- a/lib/zip_get_name.c
+++ b/lib/zip_get_name.c
@@ -1,6 +1,6 @@
 /*
   zip_get_name.c -- get filename for a file in zip file
-  Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
diff --git a/lib/zip_get_num_entries.c b/lib/zip_get_num_entries.c
index f528734..9a70375 100644
--- a/lib/zip_get_num_entries.c
+++ b/lib/zip_get_num_entries.c
@@ -1,6 +1,6 @@
 /*
   zip_get_num_entries.c -- get number of entries in archive
-  Copyright (C) 1999-2011 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
diff --git a/lib/zip_get_num_files.c b/lib/zip_get_num_files.c
index 6b28d99..224b975 100644
--- a/lib/zip_get_num_files.c
+++ b/lib/zip_get_num_files.c
@@ -1,6 +1,6 @@
 /*
   zip_get_num_files.c -- get number of files in archive
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -34,6 +34,7 @@
 
 #define _ZIP_COMPILING_DEPRECATED
 #include "zipint.h"
+#include <limits.h>
 
 
 ZIP_EXTERN int
@@ -42,6 +43,10 @@
     if (za == NULL)
 	return -1;
 
-    /* TODO: check for overflow */
+    if (za->nentry > INT_MAX) {
+	zip_error_set(&za->error, ZIP_ER_OPNOTSUPP, 0);
+	return -1;
+    }
+
     return (int)za->nentry;
 }
diff --git a/lib/zip_io_util.c b/lib/zip_io_util.c
new file mode 100644
index 0000000..3f4970c
--- /dev/null
+++ b/lib/zip_io_util.c
@@ -0,0 +1,211 @@
+/*
+ zip_io_util.c -- I/O helper functions
+ Copyright (C) 1999-2014 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>
+ 
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ 3. The names of the authors may not be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+ 
+ THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "zipint.h"
+
+zip_uint16_t
+_zip_get_16(const zip_uint8_t **a)
+{
+    zip_uint16_t ret;
+
+    ret = (zip_uint16_t)((*a)[0]+((*a)[1]<<8));
+    *a += 2;
+
+    return ret;
+}
+
+
+zip_uint32_t
+_zip_get_32(const zip_uint8_t **a)
+{
+    zip_uint32_t ret;
+
+    ret = ((((((zip_uint32_t)(*a)[3]<<8)+(*a)[2])<<8)+(*a)[1])<<8)+(*a)[0];
+    *a += 4;
+
+    return ret;
+}
+
+
+zip_uint64_t
+_zip_get_64(const zip_uint8_t **a)
+{
+    zip_uint64_t x, y;
+
+    x = ((((((zip_uint64_t)(*a)[3]<<8)+(*a)[2])<<8)+(*a)[1])<<8)+(*a)[0];
+    *a += 4;
+    y = ((((((zip_uint64_t)(*a)[3]<<8)+(*a)[2])<<8)+(*a)[1])<<8)+(*a)[0];
+    *a += 4;
+
+    return x+(y<<32);
+}
+
+int
+_zip_read(struct zip_source *src, zip_uint8_t *b, zip_uint64_t length, struct zip_error *error)
+{
+    zip_int64_t n;
+
+    if (length > ZIP_INT64_MAX) {
+	zip_error_set(error, ZIP_ER_INTERNAL, 0);
+	return -1;
+    }
+
+    if ((n = zip_source_read(src, b, length)) < 0) {
+	zip_error_set_from_source(error, src);
+	return -1;
+    }
+
+    if (n < (zip_int64_t)length) {
+	zip_error_set(error, ZIP_ER_EOF, 0);
+	return -1;
+    }
+
+    return 0;
+}
+
+
+zip_uint8_t *
+_zip_read_data(const zip_uint8_t **buf, struct zip_source *src, size_t len, int nulp, struct zip_error *error)
+{
+    zip_uint8_t *r;
+
+    if (len == 0 && nulp == 0)
+	return NULL;
+
+    r = (zip_uint8_t *)malloc(nulp ? len+1 : len);
+    if (!r) {
+	zip_error_set(error, ZIP_ER_MEMORY, 0);
+	return NULL;
+    }
+
+    if (buf) {
+	memcpy(r, *buf, len);
+	*buf += len;
+    }
+    else {
+	if (_zip_read(src, r, len, error) < 0) {
+	    free(r);
+	    return NULL;
+	}
+    }
+
+    if (nulp) {
+	zip_uint8_t *o;
+	/* replace any in-string NUL characters with spaces */
+	r[len] = 0;
+	for (o=r; o<r+len; o++)
+	    if (*o == '\0')
+		*o = ' ';
+    }
+
+    return r;
+}
+
+
+struct zip_string *
+_zip_read_string(const zip_uint8_t **buf, struct zip_source *src, zip_uint16_t len, int nulp, struct zip_error *error)
+{
+    zip_uint8_t *raw;
+    struct zip_string *s;
+
+    if ((raw=_zip_read_data(buf, src, len, nulp, error)) == NULL)
+	return NULL;
+
+    s = _zip_string_new(raw, len, ZIP_FL_ENC_GUESS, error);
+    free(raw);
+    return s;
+}
+
+
+
+
+void
+_zip_put_16(zip_uint8_t **p, zip_uint16_t i)
+{
+    *((*p)++) = i&0xff;
+    *((*p)++) = (i>>8)&0xff;
+}
+
+
+void
+_zip_put_32(zip_uint8_t **p, zip_uint32_t i)
+{
+    *((*p)++) = i&0xff;
+    *((*p)++) = (i>>8)&0xff;
+    *((*p)++) = (i>>16)&0xff;
+    *((*p)++) = (i>>24)&0xff;
+}
+
+
+void
+_zip_put_64(zip_uint8_t **p, zip_uint64_t i)
+{
+    *((*p)++) = i&0xff;
+    *((*p)++) = (i>>8)&0xff;
+    *((*p)++) = (i>>16)&0xff;
+    *((*p)++) = (i>>24)&0xff;
+    *((*p)++) = (i>>32)&0xff;
+    *((*p)++) = (i>>40)&0xff;
+    *((*p)++) = (i>>48)&0xff;
+    *((*p)++) = (i>>56)&0xff;
+}
+
+
+void
+_zip_put_data(zip_uint8_t **p, const char *s, size_t len)
+{
+    memcpy(*p, s, len);
+    *p += len;
+}
+
+
+int
+_zip_write(struct zip *za, const void *data, zip_uint64_t length)
+{
+    zip_int64_t n;
+    
+    if ((n = zip_source_write(za->src, data, length)) < 0) {
+        zip_error_set_from_source(&za->error, za->src);
+        return -1;
+    }
+    if ((zip_uint64_t)n != length) {
+        zip_error_set(&za->error, ZIP_ER_WRITE, EINTR);
+        return -1;
+    }
+    
+    return 0;
+}
diff --git a/lib/zip_memdup.c b/lib/zip_memdup.c
index 11bdfe7..758b946 100644
--- a/lib/zip_memdup.c
+++ b/lib/zip_memdup.c
@@ -1,6 +1,6 @@
 /*
   zip_memdup.c -- internal zip function, "strdup" with len
-  Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -47,7 +47,7 @@
 
     ret = malloc(len);
     if (!ret) {
-	_zip_error_set(error, ZIP_ER_MEMORY, 0);
+	zip_error_set(error, ZIP_ER_MEMORY, 0);
 	return NULL;
     }
 
diff --git a/lib/zip_name_locate.c b/lib/zip_name_locate.c
index dd011cc..3435e14 100644
--- a/lib/zip_name_locate.c
+++ b/lib/zip_name_locate.c
@@ -1,6 +1,6 @@
 /*
   zip_name_locate.c -- get index by name
-  Copyright (C) 1999-2011 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -58,7 +58,7 @@
 	return -1;
 
     if (fname == NULL) {
-	_zip_error_set(error, ZIP_ER_INVAL, 0);
+	zip_error_set(error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
@@ -83,6 +83,6 @@
 	}
     }
 
-    _zip_error_set(error, ZIP_ER_NOENT, 0);
+    zip_error_set(error, ZIP_ER_NOENT, 0);
     return -1;
 }
diff --git a/lib/zip_new.c b/lib/zip_new.c
index db64be3..484ee75 100644
--- a/lib/zip_new.c
+++ b/lib/zip_new.c
@@ -48,22 +48,21 @@
 
     za = (struct zip *)malloc(sizeof(struct zip));
     if (!za) {
-	_zip_error_set(error, ZIP_ER_MEMORY, 0);
+	zip_error_set(error, ZIP_ER_MEMORY, 0);
 	return NULL;
     }
 
-    za->zn = NULL;
-    za->zp = NULL;
+    za->src = NULL;
     za->open_flags = 0;
-    _zip_error_init(&za->error);
+    zip_error_init(&za->error);
     za->flags = za->ch_flags = 0;
     za->default_password = NULL;
     za->comment_orig = za->comment_changes = NULL;
     za->comment_changed = 0;
     za->nentry = za->nentry_alloc = 0;
     za->entry = NULL;
-    za->nsource = za->nsource_alloc = 0;
-    za->source = NULL;
+    za->nopen_source = za->nopen_source_alloc = 0;
+    za->open_source = NULL;
     za->tempdir = NULL;
     
     return za;
diff --git a/lib/zip_open.c b/lib/zip_open.c
index 30afde9..553a37e 100644
--- a/lib/zip_open.c
+++ b/lib/zip_open.c
@@ -1,6 +1,6 @@
 /*
   zip_open.c -- open zip archive by name
-  Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -41,68 +41,126 @@
 
 #include "zipint.h"
 
-static void set_error(int *, const struct zip_error *, int);
-static struct zip *_zip_allocate_new(const char *, unsigned int, int *);
-static zip_int64_t _zip_checkcons(FILE *, struct zip_cdir *, struct zip_error *);
-static void _zip_check_torrentzip(struct zip *, const struct zip_cdir *);
-static struct zip_cdir *_zip_find_central_dir(FILE *, unsigned int, int *, off_t);
-static int _zip_file_exists(const char *, unsigned int, int *);
+typedef enum {
+    EXISTS_ERROR = -1,
+    EXISTS_NOT = 0,
+    EXISTS_EMPTY,
+    EXISTS_NONEMPTY,
+} exists_t;
+static struct zip *_zip_allocate_new(struct zip_source *src, unsigned int flags, struct zip_error *error);
+static zip_int64_t _zip_checkcons(struct zip *za, struct zip_cdir *cdir, struct zip_error *error);
+static void _zip_check_torrentzip(struct zip *za, const struct zip_cdir *cdir);
+static struct zip_cdir *_zip_find_central_dir(struct zip *za, zip_uint64_t len);
+static exists_t _zip_file_exists(struct zip_source *src, struct zip_error *error);
 static int _zip_headercomp(const struct zip_dirent *, const struct zip_dirent *);
 static unsigned char *_zip_memmem(const unsigned char *, size_t,
 				  const unsigned char *, size_t);
-static struct zip_cdir *_zip_readcdir(FILE *, off_t, unsigned char *, const unsigned char *,
-				 size_t, unsigned int, struct zip_error *);
-static struct zip_cdir *_zip_read_eocd(const unsigned char *, const unsigned char *, off_t,
+static struct zip_cdir *_zip_read_cdir(zip_t *za, zip_uint64_t buf_offset, zip_uint8_t *buf, const zip_uint8_t *eocd, size_t buflen, struct zip_error *error);
+static struct zip_cdir *_zip_read_eocd(const unsigned char *, const unsigned char *, zip_uint64_t,
 				       size_t, unsigned int, struct zip_error *);
-static struct zip_cdir *_zip_read_eocd64(FILE *, const unsigned char *, const unsigned char *,
-					 off_t, size_t, unsigned int, struct zip_error *);
+static struct zip_cdir *_zip_read_eocd64(struct zip_source *, const zip_uint8_t *, const zip_uint8_t *,
+                                         zip_uint64_t, size_t, unsigned int, struct zip_error *);
 
 
 ZIP_EXTERN struct zip *
 zip_open(const char *fn, int _flags, int *zep)
 {
-    FILE *fp;
-    unsigned int flags;
+    struct zip *za;
+    struct zip_source *src;
+    struct zip_error error;
+
+    zip_error_init(&error);
+    if ((src = zip_source_file_create(fn, 0, -1, &error)) == NULL) {
+	_zip_set_open_error(zep, &error, 0);
+	zip_error_fini(&error);
+	return NULL;
+    }
+
+    if ((za = zip_open_from_source(src, _flags, &error)) == NULL) {
+	_zip_set_open_error(zep, &error, 0);
+	zip_error_fini(&error);
+	return NULL;
+    }
+
+    zip_error_fini(&error);
+    return za;
+}
+
+
+ZIP_EXTERN struct zip *
+zip_open_from_source(struct zip_source *src, int _flags, struct zip_error *error)
+{
+    static zip_int64_t needed_support_read = -1;
+    static zip_int64_t needed_support_write = -1;
     
-    if (_flags < 0 || fn == NULL) {
-        if (zep)
-            *zep = ZIP_ER_INVAL;
+    unsigned int flags;
+    zip_int64_t supported;
+    exists_t exists;
+    
+    if (_flags < 0 || src == NULL) {
+	zip_error_set(error, ZIP_ER_INVAL, 0);
         return NULL;
     }
     flags = (unsigned int)_flags;
+    
+    supported = zip_source_supports(src);
+    if (needed_support_read == -1) {
+        needed_support_read = zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_SEEK, ZIP_SOURCE_TELL, ZIP_SOURCE_STAT, -1);
+        needed_support_write = zip_source_make_command_bitmap(ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_COMMIT_WRITE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_REMOVE, -1);
+    }
+    if ((supported & needed_support_read) != needed_support_read) {
+        zip_error_set(error, ZIP_ER_OPNOTSUPP, 0);
+        return NULL;
+    }
+    if ((supported & needed_support_write) != needed_support_write) {
+        flags |= ZIP_RDONLY;
+    }
 
-    switch (_zip_file_exists(fn, flags, zep)) {
-    case -1:
+    if ((flags & (ZIP_RDONLY|ZIP_TRUNCATE)) == (ZIP_RDONLY|ZIP_TRUNCATE)) {
+	zip_error_set(error, ZIP_ER_RDONLY, 0);
+	return NULL;
+    }
+
+    exists = _zip_file_exists(src, error);
+    switch (exists) { 
+    case EXISTS_ERROR:
 	return NULL;
 
-    case 0:
+    case EXISTS_NOT:
 	if ((flags & ZIP_CREATE) == 0) {
-	    set_error(zep, NULL, ZIP_ER_NOENT);
+	    zip_error_set(error, ZIP_ER_NOENT, 0);
 	    return NULL;
 	}
-	return _zip_allocate_new(fn, flags, zep);
+	return _zip_allocate_new(src, flags, error);
 
-    default:
+    default: {
+	zip_t *za;
 	if (flags & ZIP_EXCL) {
-	    set_error(zep, NULL, ZIP_ER_EXISTS);
+	    zip_error_set(error, ZIP_ER_EXISTS, 0);
+	    return NULL;
+	}
+	if (zip_source_open(src) < 0) {
+	    zip_error_set_from_source(error, src);
 	    return NULL;
 	}
 
-	if ((fp = fopen(fn, "rb")) == NULL) {
-	    set_error(zep, NULL, ZIP_ER_OPEN);
+	if (flags & ZIP_TRUNCATE) {
+	    za = _zip_allocate_new(src, flags, error);
+	}
+	else {
+	    /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL, just like open() */
+	    za = _zip_open(src, flags, error);
+	}
+
+	if (za == NULL) {
+	    zip_source_close(src);
 	    return NULL;
 	}
-	if (flags & ZIP_TRUNCATE) {
-	    fclose(fp);
-	    return _zip_allocate_new(fn, flags, zep);
-	}
-	/* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL,
-	   just like open() */
-	return _zip_open(fn, fp, flags, zep);
+	return za;
+    }
     }
 }
 
-
 ZIP_EXTERN int
 zip_archive_set_tempdir(struct zip *za, const char *tempdir)
 {
@@ -110,7 +168,7 @@
     
     if (tempdir) {
         if ((new_tempdir = strdup(tempdir)) == NULL) {
-            _zip_error_set(&za->error, ZIP_ER_MEMORY, errno);
+            zip_error_set(&za->error, ZIP_ER_MEMORY, errno);
             return -1;
         }
     }
@@ -124,36 +182,40 @@
 }
 
 struct zip *
-_zip_open(const char *fn, FILE *fp, unsigned int flags, int *zep)
+_zip_open(struct zip_source *src, unsigned int flags, struct zip_error *error)
 {
     struct zip *za;
     struct zip_cdir *cdir;
-    off_t len;
+    struct zip_stat st;
+    zip_uint64_t len;
 
-    if (fseeko(fp, 0, SEEK_END) < 0) {
-	*zep = ZIP_ER_SEEK;
+    zip_stat_init(&st);
+    if (zip_source_stat(src, &st) < 0) {
+	zip_error_set_from_source(error, src);
 	return NULL;
     }
-    len = ftello(fp);
+    if ((st.valid & ZIP_STAT_SIZE) == 0) {
+	zip_error_set(error, ZIP_ER_SEEK, EOPNOTSUPP);
+	return NULL;
+    }
+    len = st.size;
 
     /* treat empty files as empty archives */
     if (len == 0) {
-	if ((za=_zip_allocate_new(fn, flags, zep)) == NULL)
-	    fclose(fp);
-	else
-	    za->zp = fp;
+	if ((za=_zip_allocate_new(src, flags, error)) == NULL) {
+	    zip_source_free(src);
+	    return NULL;
+	}
+
 	return za;
     }
 
-    cdir = _zip_find_central_dir(fp, flags, zep, len);
-    if (cdir == NULL) {
-	fclose(fp);
-	return NULL;
+    if ((za=_zip_allocate_new(src, flags, error)) == NULL) {
+        return NULL;
     }
-
-    if ((za=_zip_allocate_new(fn, flags, zep)) == NULL) {
-	_zip_cdir_free(cdir);
-	fclose(fp);
+    
+    if ((cdir = _zip_find_central_dir(za, len)) == NULL) {
+        _zip_error_copy(error, &za->error);
 	return NULL;
     }
 
@@ -162,8 +224,6 @@
     za->nentry_alloc = cdir->nentry_alloc;
     za->comment_orig = cdir->comment;
     
-    za->zp = fp;
-
     _zip_check_torrentzip(za, cdir);
 
     za->ch_flags = za->flags;
@@ -174,15 +234,14 @@
 }
 
 
-static void
-set_error(int *zep, const struct zip_error *err, int ze)
+void
+_zip_set_open_error(int *zep, const struct zip_error *err, int ze)
 {
-    int se;
-
     if (err) {
-	_zip_error_get(err, &ze, &se);
-	if (zip_error_get_sys_type(ze) == ZIP_ET_SYS)
-	    errno = se;
+	ze = zip_error_code_zip(err);
+	if (zip_error_system_type(err) == ZIP_ET_SYS) {
+	    errno = zip_error_code_system(err);
+	}
     }
 
     if (zep)
@@ -197,8 +256,7 @@
    entries, or NULL if unsuccessful. */
 
 static struct zip_cdir *
-_zip_readcdir(FILE *fp, off_t buf_offset, unsigned char *buf, const unsigned char *eocd, size_t buflen,
-	      unsigned int flags, struct zip_error *error)
+_zip_read_cdir(zip_t *za, zip_uint64_t buf_offset, zip_uint8_t *buf, const zip_uint8_t *eocd, size_t buflen, struct zip_error *error)
 {
     struct zip_cdir *cd;
     const unsigned char *cdp;
@@ -209,41 +267,41 @@
     tail_len = buf + buflen - eocd - EOCDLEN;
     if (tail_len < 0) {
 	/* not enough bytes left for comment */
-	_zip_error_set(error, ZIP_ER_NOZIP, 0);
+	zip_error_set(error, ZIP_ER_NOZIP, 0);
 	return NULL;
     }
 
     /* check for end-of-central-dir magic */
     if (memcmp(eocd, EOCD_MAGIC, 4) != 0) {
-	_zip_error_set(error, ZIP_ER_NOZIP, 0);
+	zip_error_set(error, ZIP_ER_NOZIP, 0);
 	return NULL;
     }
 
     if (memcmp(eocd+4, "\0\0\0\0", 4) != 0) {
-	_zip_error_set(error, ZIP_ER_MULTIDISK, 0);
+	zip_error_set(error, ZIP_ER_MULTIDISK, 0);
 	return NULL;
     }
 
     if (eocd-EOCD64LOCLEN >= buf && memcmp(eocd-EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0)
-	cd = _zip_read_eocd64(fp, eocd-EOCD64LOCLEN, buf, buf_offset, buflen, flags, error);
+	cd = _zip_read_eocd64(za->src, eocd-EOCD64LOCLEN, buf, buf_offset, buflen, za->flags, error);
     else
-	cd = _zip_read_eocd(eocd, buf, buf_offset, buflen, flags, error);
+	cd = _zip_read_eocd(eocd, buf, buf_offset, buflen, za->flags, error);
 
     if (cd == NULL)
 	return NULL;
 
     cdp = eocd + 20;
-    comment_len = _zip_read2(&cdp);
+    comment_len = _zip_get_16(&cdp);
 
     if ((zip_uint64_t)cd->offset+(zip_uint64_t)cd->size > (zip_uint64_t)buf_offset + (zip_uint64_t)(eocd-buf)) {
 	/* cdir spans past EOCD record */
-	_zip_error_set(error, ZIP_ER_INCONS, 0);
+	zip_error_set(error, ZIP_ER_INCONS, 0);
 	_zip_cdir_free(cd);
 	return NULL;
     }
 
-    if (tail_len < comment_len || ((flags & ZIP_CHECKCONS) && tail_len != comment_len)) {
-	_zip_error_set(error, ZIP_ER_INCONS, 0);
+    if (tail_len < comment_len || ((za->open_flags & ZIP_CHECKCONS) && tail_len != comment_len)) {
+	zip_error_set(error, ZIP_ER_INCONS, 0);
 	_zip_cdir_free(cd);
 	return NULL;
     }
@@ -263,16 +321,16 @@
     else {
 	/* go to start of cdir and read it entry by entry */
 	bufp = NULL;
-	clearerr(fp);
-	fseeko(fp, cd->offset, SEEK_SET);
-	/* possible consistency check: cd->offset =
-	   len-(cd->size+cd->comment_len+EOCDLEN) ? */
-	if (ferror(fp) || (ftello(fp) != cd->offset)) {
-	    /* seek error or offset of cdir wrong */
-	    if (ferror(fp))
-		_zip_error_set(error, ZIP_ER_SEEK, errno);
-	    else
-		_zip_error_set(error, ZIP_ER_NOZIP, 0);
+        
+        if (zip_source_seek(za->src, (zip_int64_t)cd->offset, SEEK_SET) < 0) {
+            zip_error_set_from_source(error, za->src);
+            _zip_cdir_free(cd);
+            return NULL;
+        }
+
+	/* possible consistency check: cd->offset = len-(cd->size+cd->comment_len+EOCDLEN) ? */
+	if (zip_source_tell(za->src) != (zip_int64_t)cd->offset) {
+            zip_error_set(error, ZIP_ER_NOZIP, 0);
 	    _zip_cdir_free(cd);
 	    return NULL;
 	}
@@ -282,14 +340,14 @@
     i=0;
     while (i<cd->nentry && left > 0) {
 	if ((cd->entry[i].orig=_zip_dirent_new()) == NULL
-	    || (_zip_dirent_read(cd->entry[i].orig, fp, bufp, &left, 0, error)) < 0) {
+	    || (_zip_dirent_read(cd->entry[i].orig, za->src, bufp, &left, 0, error)) < 0) {
 	    _zip_cdir_free(cd);
 	    return NULL;
 	}
 	i++;
     }
-    if (i != cd->nentry || ((flags & ZIP_CHECKCONS) && left != 0)) {
-        _zip_error_set(error, ZIP_ER_INCONS, 0);
+    if (i != cd->nentry || ((za->open_flags & ZIP_CHECKCONS) && left != 0)) {
+        zip_error_set(error, ZIP_ER_INCONS, 0);
         _zip_cdir_free(cd);
         return NULL;
     }
@@ -305,7 +363,7 @@
    difference between the lowest and the highest fileposition reached */
 
 static zip_int64_t
-_zip_checkcons(FILE *fp, struct zip_cdir *cd, struct zip_error *error)
+_zip_checkcons(struct zip *za, struct zip_cdir *cd, struct zip_error *error)
 {
     zip_uint64_t i;
     zip_uint64_t min, max, j;
@@ -322,7 +380,7 @@
 	if (cd->entry[i].orig->offset < min)
 	    min = cd->entry[i].orig->offset;
 	if (min > (zip_uint64_t)cd->offset) {
-	    _zip_error_set(error, ZIP_ER_NOZIP, 0);
+	    zip_error_set(error, ZIP_ER_NOZIP, 0);
 	    return -1;
 	}
 	
@@ -331,20 +389,20 @@
 	if (j > max)
 	    max = j;
 	if (max > (zip_uint64_t)cd->offset) {
-	    _zip_error_set(error, ZIP_ER_NOZIP, 0);
+	    zip_error_set(error, ZIP_ER_NOZIP, 0);
 	    return -1;
 	}
 	
-	if (fseeko(fp, (off_t)cd->entry[i].orig->offset, SEEK_SET) != 0) {
-	    _zip_error_set(error, ZIP_ER_SEEK, errno);
-	    return -1;
+        if (zip_source_seek(za->src, (zip_int64_t)cd->entry[i].orig->offset, SEEK_SET) < 0) {
+            zip_error_set_from_source(error, za->src);
+            return -1;
 	}
 	
-	if (_zip_dirent_read(&temp, fp, NULL, NULL, 1, error) == -1)
+	if (_zip_dirent_read(&temp, za->src, NULL, NULL, 1, error) == -1)
 	    return -1;
 	
 	if (_zip_headercomp(cd->entry[i].orig, &temp) != 0) {
-	    _zip_error_set(error, ZIP_ER_INCONS, 0);
+	    zip_error_set(error, ZIP_ER_INCONS, 0);
 	    _zip_dirent_finalize(&temp);
 	    return -1;
 	}
@@ -370,7 +428,7 @@
     char buf[8+1];
     char *end;
 
-    if (za->zp == NULL || cdir == NULL)
+    if (cdir == NULL)
 	return;
 
     if (_zip_string_length(cdir->comment) != TORRENT_SIG_LEN+8
@@ -384,7 +442,7 @@
     if ((crc_should == UINT_MAX && errno != 0) || (end && *end))
 	return;
 
-    if (_zip_filerange_crc(za->zp, cdir->offset, cdir->size, &crc_got, NULL) < 0)
+    if (_zip_filerange_crc(za->src, cdir->offset, cdir->size, &crc_got, NULL) < 0)
 	return;
 
     if (crc_got == crc_should)
@@ -426,116 +484,103 @@
 
 
 static struct zip *
-_zip_allocate_new(const char *fn, unsigned int flags, int *zep)
+_zip_allocate_new(struct zip_source *src, unsigned int flags, struct zip_error *error)
 {
     struct zip *za;
-    struct zip_error error;
 
-    if ((za=_zip_new(&error)) == NULL) {
-	set_error(zep, &error, 0);
+    if ((za = _zip_new(error)) == NULL) {
 	return NULL;
     }
 
-    if (fn == NULL)
-	za->zn = NULL;
-    else {
-	za->zn = strdup(fn);
-	if (!za->zn) {
-	    zip_discard(za);
-	    set_error(zep, NULL, ZIP_ER_MEMORY);
-	    return NULL;
-	}
-    }
+    za->src = src;
     za->open_flags = flags;
+    if (flags & ZIP_RDONLY) {
+        za->flags |= ZIP_AFL_RDONLY;
+        za->ch_flags |= ZIP_AFL_RDONLY;
+    }
     return za;
 }
 
 
 /*
  * tests for file existence
- * 1: file exists and is stat()able
- * 0: file does not exist
- * -1: error
  */
-static int
-_zip_file_exists(const char *fn, unsigned int flags, int *zep)
+static exists_t
+_zip_file_exists(struct zip_source *src, struct zip_error *error)
 {
-    struct stat st;
+    struct zip_stat st;
 
-    if (fn == NULL) {
-	set_error(zep, NULL, ZIP_ER_INVAL);
-	return -1;
-    }
-    
-    if (stat(fn, &st) != 0) {
-	if (errno == ENOENT) {
-	    return 0;
+    zip_stat_init(&st);
+    if (zip_source_stat(src, &st) != 0) {
+        zip_error_t *src_error = zip_source_error(src);
+        if (zip_error_code_zip(src_error) == ZIP_ER_READ && zip_error_code_system(src_error) == ENOENT) {
+	    return EXISTS_NOT;
 	}
-	set_error(zep, NULL, ZIP_ER_OPEN);
-	return -1;
+	_zip_error_copy(error, src_error);
+	return EXISTS_ERROR;
     }
 
-    return 1;
+    return (st.valid & ZIP_STAT_SIZE) && st.size == 0 ? EXISTS_EMPTY : EXISTS_NONEMPTY;
 }
 
 
 static struct zip_cdir *
-_zip_find_central_dir(FILE *fp, unsigned int flags, int *zep, off_t len)
+_zip_find_central_dir(struct zip *za, zip_uint64_t len)
 {
     struct zip_cdir *cdir, *cdirnew;
     unsigned char *buf, *match;
-    off_t buf_offset;
-    size_t buflen;
-    zip_int64_t a, i;
+    zip_int64_t buf_offset;
+    zip_uint64_t buflen;
+    zip_int64_t a;
     zip_int64_t best;
     struct zip_error zerr;
 
-    if (len < (off_t)EOCDLEN) {
-        set_error(zep, NULL, ZIP_ER_NOZIP);
+    if (len < EOCDLEN) {
+	zip_error_set(&za->error, ZIP_ER_NOZIP, 0);
+        return NULL;
+    }
+
+    buflen = (len < CDBUFSIZE ? len : CDBUFSIZE);
+    if (zip_source_seek(za->src, -(zip_int64_t)buflen, SEEK_END) < 0) {
+	struct zip_error *src_error = zip_source_error(za->src);
+	if (zip_error_code_zip(src_error) != ZIP_ER_SEEK || zip_error_code_system(src_error) != EFBIG) {
+	    /* seek before start of file on my machine */
+	    _zip_error_copy(&za->error, src_error);
+	    return NULL;
+	}
+    }
+    if ((buf_offset = zip_source_tell(za->src)) < 0) {
+        zip_error_set_from_source(&za->error, za->src);
         return NULL;
     }
     
-    i = fseeko(fp, -(len < CDBUFSIZE ? len : CDBUFSIZE), SEEK_END);
-    if (i == -1 && errno != EFBIG) {
-	/* seek before start of file on my machine */
-	set_error(zep, NULL, ZIP_ER_SEEK);
-	return NULL;
-    }
-    buf_offset = ftello(fp);
-    
     /* 64k is too much for stack */
-    if ((buf=(unsigned char *)malloc(CDBUFSIZE)) == NULL) {
-	set_error(zep, NULL, ZIP_ER_MEMORY);
+    if ((buf=(unsigned char *)malloc(buflen)) == NULL) {
+	zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
 	return NULL;
     }
-
-    clearerr(fp);
-    buflen = fread(buf, 1, CDBUFSIZE, fp);
-
-    if (ferror(fp)) {
-	set_error(zep, NULL, ZIP_ER_READ);
-	free(buf);
+    
+    if (_zip_read(za->src, buf, buflen, &za->error) < 0) {
 	return NULL;
     }
     
     best = -1;
     cdir = NULL;
-    match = buf+ (buflen < CDBUFSIZE ? 0 : EOCD64LOCLEN);
-    _zip_error_set(&zerr, ZIP_ER_NOZIP, 0);
+    match = buf + (buflen < CDBUFSIZE ? 0 : EOCD64LOCLEN);
+    zip_error_set(&zerr, ZIP_ER_NOZIP, 0);
 
     while ((match=_zip_memmem(match, buflen-(size_t)(match-buf)-(EOCDLEN-4),
 			      (const unsigned char *)EOCD_MAGIC, 4))!=NULL) {
 	/* found match -- check, if good */
 	/* to avoid finding the same match all over again */
 	match++;
-	if ((cdirnew=_zip_readcdir(fp, buf_offset, buf, match-1, buflen, flags,
-				   &zerr)) == NULL)
+	if ((cdirnew=_zip_read_cdir(za, (zip_uint64_t)buf_offset, buf, match-1, buflen, &zerr)) == NULL)
 	    continue;
 
 	if (cdir) {
 	    if (best <= 0)
-		best = _zip_checkcons(fp, cdir, &zerr);
-	    a = _zip_checkcons(fp, cdirnew, &zerr);
+		best = _zip_checkcons(za, cdir, &zerr);
+	    a = _zip_checkcons(za, cdirnew, &zerr);
 	    if (best < a) {
 		_zip_cdir_free(cdir);
 		cdir = cdirnew;
@@ -546,8 +591,8 @@
 	}
 	else {
 	    cdir = cdirnew;
-	    if (flags & ZIP_CHECKCONS)
-		best = _zip_checkcons(fp, cdir, &zerr);
+	    if (za->open_flags & ZIP_CHECKCONS)
+		best = _zip_checkcons(za, cdir, &zerr);
 	    else
 		best = 0;
 	}
@@ -557,7 +602,7 @@
     free(buf);
     
     if (best < 0) {
-	set_error(zep, &zerr, 0);
+	_zip_error_copy(&za->error, &zerr);
 	_zip_cdir_free(cdir);
 	return NULL;
     }
@@ -585,7 +630,7 @@
 
 
 static struct zip_cdir *
-_zip_read_eocd(const unsigned char *eocd, const unsigned char *buf, off_t buf_offset, size_t buflen,
+_zip_read_eocd(const unsigned char *eocd, const unsigned char *buf, zip_uint64_t buf_offset, size_t buflen,
 	       unsigned int flags, struct zip_error *error)
 {
     struct zip_cdir *cd;
@@ -593,54 +638,54 @@
     zip_uint64_t i, nentry, size, offset;
 
     if (eocd+EOCDLEN > buf+buflen) {
-	_zip_error_set(error, ZIP_ER_INCONS, 0);
+	zip_error_set(error, ZIP_ER_INCONS, 0);
 	return NULL;
     }
 
     cdp = eocd + 8;
 
     /* number of cdir-entries on this disk */
-    i = _zip_read2(&cdp);
+    i = _zip_get_16(&cdp);
     /* number of cdir-entries */
-    nentry = _zip_read2(&cdp);
+    nentry = _zip_get_16(&cdp);
 
     if (nentry != i) {
-	_zip_error_set(error, ZIP_ER_NOZIP, 0);
+	zip_error_set(error, ZIP_ER_NOZIP, 0);
 	return NULL;
     }
 
-    size = _zip_read4(&cdp);
-    offset = _zip_read4(&cdp);
+    size = _zip_get_32(&cdp);
+    offset = _zip_get_32(&cdp);
 
-    if (size > ZIP_OFF_MAX || offset > ZIP_OFF_MAX || offset+size > ZIP_OFF_MAX) {
-        _zip_error_set(error, ZIP_ER_SEEK, EFBIG);
+    if (offset+size < offset) {
+        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
         return NULL;
     }
     
-    if (offset+size > (zip_uint64_t)(buf_offset + (eocd-buf))) {
+    if (offset+size > buf_offset + (zip_uint64_t)(eocd-buf)) {
 	/* cdir spans past EOCD record */
-	_zip_error_set(error, ZIP_ER_INCONS, 0);
+	zip_error_set(error, ZIP_ER_INCONS, 0);
 	return NULL;
     }
 
-    if ((flags & ZIP_CHECKCONS) && offset+size != (zip_uint64_t)(buf_offset + (eocd-buf))) {
-	_zip_error_set(error, ZIP_ER_INCONS, 0);
+    if ((flags & ZIP_CHECKCONS) && offset+size != buf_offset + (zip_uint64_t)(eocd-buf)) {
+	zip_error_set(error, ZIP_ER_INCONS, 0);
 	return NULL;
     }
 
     if ((cd=_zip_cdir_new(nentry, error)) == NULL)
 	return NULL;
 
-    cd->size = (off_t)size;
-    cd->offset = (off_t)offset;
+    cd->size = size;
+    cd->offset = offset;
     
     return cd;
 }
 
 
 static struct zip_cdir *
-_zip_read_eocd64(FILE *f, const zip_uint8_t *eocd64loc, const zip_uint8_t *buf,
-		 off_t buf_offset, size_t buflen, unsigned int flags, struct zip_error *error)
+_zip_read_eocd64(struct zip_source *src, const zip_uint8_t *eocd64loc, const zip_uint8_t *buf,
+		 zip_uint64_t buf_offset, size_t buflen, unsigned int flags, struct zip_error *error)
 {
     struct zip_cdir *cd;
     zip_uint64_t offset;
@@ -650,73 +695,73 @@
     zip_uint64_t size, nentry, i;
 
     cdp = eocd64loc+8;
-    eocd_offset = _zip_read8(&cdp);
+    eocd_offset = _zip_get_64(&cdp);
     
-    if (eocd_offset > ZIP_OFF_MAX || eocd_offset + EOCD64LEN > ZIP_OFF_MAX) {
-        _zip_error_set(error, ZIP_ER_SEEK, EFBIG);
+    if (eocd_offset > ZIP_INT64_MAX || eocd_offset + EOCD64LEN < eocd_offset) {
+        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
         return NULL;
     }
 
-    if (eocd64loc < buf || (off_t)eocd_offset+EOCD64LEN > (buf_offset+(eocd64loc-buf))) {
-	_zip_error_set(error, ZIP_ER_INCONS, 0);
+    if (eocd64loc < buf || eocd_offset+EOCD64LEN > buf_offset+(zip_uint64_t)(eocd64loc-buf)) {
+	zip_error_set(error, ZIP_ER_INCONS, 0);
 	return NULL;
     }
 
-    if ((off_t)eocd_offset >= buf_offset && (off_t)eocd_offset+EOCD64LEN <= buf_offset+(ssize_t)buflen)
-	cdp = buf+((off_t)eocd_offset-buf_offset);
+    if (eocd_offset >= buf_offset && eocd_offset+EOCD64LEN <= buf_offset+buflen)
+	cdp = buf+(eocd_offset-buf_offset);
     else {
-	if (fseeko(f, (off_t)eocd_offset, SEEK_SET) != 0) {
-	    _zip_error_set(error, ZIP_ER_SEEK, errno);
+	zip_int64_t n;
+
+	if (zip_source_seek(src, (zip_int64_t)eocd_offset, SEEK_SET) < 0) {
+	    zip_error_set_from_source(error, src);
 	    return NULL;
 	}
 
-	clearerr(f);
-	if (fread(eocd, 1, EOCD64LEN, f) < EOCD64LEN) {
-            _zip_error_set(error, ZIP_ER_READ, errno);
+	if ((n = zip_source_read(src, eocd, EOCD64LEN)) < 0) {
+	    zip_error_set_from_source(error, src);
+	    return NULL;
+	}
+	if (n < EOCD64LEN) {
+            zip_error_set(error, ZIP_ER_READ, EIO);
             return NULL;
         }
 
-	if (ferror(f)) {
-	    _zip_error_set(error, ZIP_ER_READ, errno);
-	    return NULL;
-	}
-
 	cdp = eocd;
     }
 
     if (memcmp(cdp, EOCD64_MAGIC, 4) != 0) {
-	_zip_error_set(error, ZIP_ER_INCONS, 0);
+	zip_error_set(error, ZIP_ER_INCONS, 0);
 	return NULL;
     }
     cdp += 4;
     
-    size = _zip_read8(&cdp);
+    size = _zip_get_64(&cdp);
 
-    if ((flags & ZIP_CHECKCONS) && size+eocd_offset+12 != (zip_uint64_t)(buf_offset+(eocd64loc-buf))) {
-	_zip_error_set(error, ZIP_ER_INCONS, 0);
+    if ((flags & ZIP_CHECKCONS) && size+eocd_offset+12 != buf_offset+(zip_uint64_t)(eocd64loc-buf)) {
+	zip_error_set(error, ZIP_ER_INCONS, 0);
 	return NULL;
     }
 
     cdp += 4; /* skip version made by/needed */
     cdp += 8; /* skip num disks */
     
-    nentry = _zip_read8(&cdp);
-    i = _zip_read8(&cdp);
+    nentry = _zip_get_64(&cdp);
+    i = _zip_get_64(&cdp);
 
     if (nentry != i) {
-	_zip_error_set(error, ZIP_ER_MULTIDISK, 0);
+	zip_error_set(error, ZIP_ER_MULTIDISK, 0);
 	return NULL;
     }
 
-    size = _zip_read8(&cdp);
-    offset = _zip_read8(&cdp);
+    size = _zip_get_64(&cdp);
+    offset = _zip_get_64(&cdp);
 
-    if (size > ZIP_OFF_MAX || offset > ZIP_OFF_MAX || offset+size > ZIP_OFF_MAX) {
-        _zip_error_set(error, ZIP_ER_SEEK, EFBIG);
+    if (offset > ZIP_INT64_MAX || offset+size < offset) {
+        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
         return NULL;
     }
     if ((flags & ZIP_CHECKCONS) && offset+size != eocd_offset) {
-	_zip_error_set(error, ZIP_ER_INCONS, 0);
+	zip_error_set(error, ZIP_ER_INCONS, 0);
 	return NULL;
     }
 
@@ -724,8 +769,8 @@
 	return NULL;
 
     
-    cd->size = (off_t)size;
-    cd->offset = (off_t)offset;
+    cd->size = size;
+    cd->offset = offset;
 
     return cd;
 }
diff --git a/lib/zip_rename.c b/lib/zip_rename.c
index 1c77b0d..ecc7705 100644
--- a/lib/zip_rename.c
+++ b/lib/zip_rename.c
@@ -1,6 +1,6 @@
 /*
   zip_rename.c -- rename file in zip archive
-  Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
diff --git a/lib/zip_replace.c b/lib/zip_replace.c
index 2d699ad..5838252 100644
--- a/lib/zip_replace.c
+++ b/lib/zip_replace.c
@@ -1,6 +1,6 @@
 /*
   zip_replace.c -- replace file via callback function
-  Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
diff --git a/lib/zip_set_archive_comment.c b/lib/zip_set_archive_comment.c
index 7b8b24b..2153138 100644
--- a/lib/zip_set_archive_comment.c
+++ b/lib/zip_set_archive_comment.c
@@ -1,6 +1,6 @@
 /*
   zip_set_archive_comment.c -- set archive comment
-  Copyright (C) 2006-2013 Dieter Baron and Thomas Klausner
+  Copyright (C) 2006-2014 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>
@@ -43,12 +43,12 @@
     struct zip_string *cstr;
 
     if (ZIP_IS_RDONLY(za)) {
-	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
 	return -1;
     }
 
     if (len > 0 && comment == NULL) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
@@ -58,7 +58,7 @@
 
 	if (_zip_guess_encoding(cstr, ZIP_ENCODING_UNKNOWN) == ZIP_ENCODING_CP437) {
 	    _zip_string_free(cstr);
-	    _zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	    zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	    return -1;
 	}
     }
diff --git a/lib/zip_set_archive_flag.c b/lib/zip_set_archive_flag.c
index 8e4622d..7f555c3 100644
--- a/lib/zip_set_archive_flag.c
+++ b/lib/zip_set_archive_flag.c
@@ -1,6 +1,6 @@
 /*
   zip_get_archive_flag.c -- set archive global flag
-  Copyright (C) 2008-2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 2008-2014 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>
@@ -49,14 +49,14 @@
 	return 0;
 
     if (ZIP_IS_RDONLY(za)) {
-	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
 	return -1;
     }
 
     if ((flag & ZIP_AFL_RDONLY) && value
 	&& (za->ch_flags & ZIP_AFL_RDONLY) == 0) {
 	if (_zip_changed(za, NULL)) {
-	    _zip_error_set(&za->error, ZIP_ER_CHANGED, 0);
+	    zip_error_set(&za->error, ZIP_ER_CHANGED, 0);
 	    return -1;
 	}
     }
diff --git a/lib/zip_set_default_password.c b/lib/zip_set_default_password.c
index f41e8f8..f45d580 100644
--- a/lib/zip_set_default_password.c
+++ b/lib/zip_set_default_password.c
@@ -1,6 +1,6 @@
 /*
   zip_set_default_password.c -- set default password for decryption
-  Copyright (C) 2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 2009-2014 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>
@@ -49,7 +49,7 @@
     
     if (passwd) {
 	if ((za->default_password=strdup(passwd)) == NULL) {
-	    _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+	    zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
 	    return -1;
 	}
     }
diff --git a/lib/zip_set_file_comment.c b/lib/zip_set_file_comment.c
index e4a7d3e..fffff46 100644
--- a/lib/zip_set_file_comment.c
+++ b/lib/zip_set_file_comment.c
@@ -1,6 +1,6 @@
 /*
   zip_set_file_comment.c -- set comment for file in archive
-  Copyright (C) 2006-2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 2006-2014 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>
@@ -42,7 +42,7 @@
 zip_set_file_comment(struct zip *za, zip_uint64_t idx, const char *comment, int len)
 {
     if (len < 0 || len > ZIP_UINT16_MAX) {
-        _zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+        zip_error_set(&za->error, ZIP_ER_INVAL, 0);
         return -1;
     }
     return zip_file_set_comment(za, idx, comment, (zip_uint16_t)len, 0);
diff --git a/lib/zip_set_file_compression.c b/lib/zip_set_file_compression.c
index 3322106..41aaa67 100644
--- a/lib/zip_set_file_compression.c
+++ b/lib/zip_set_file_compression.c
@@ -1,6 +1,6 @@
 /*
   zip_set_file_compression.c -- set compression for file in archive
-  Copyright (C) 2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 2012-2014 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>
@@ -43,17 +43,17 @@
     zip_int32_t old_method;
 
     if (idx >= za->nentry) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
     if (ZIP_IS_RDONLY(za)) {
-	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
 	return -1;
     }
 
     if (method != ZIP_CM_DEFAULT && method != ZIP_CM_STORE && method != ZIP_CM_DEFLATE) {
-	_zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
+	zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
 	return -1;
     }
 
@@ -75,7 +75,7 @@
     else {
         if (e->changes == NULL) {
             if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) {
-                _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+                zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
                 return -1;
             }
         }
diff --git a/lib/zip_set_name.c b/lib/zip_set_name.c
index f92fe90..87d372f 100644
--- a/lib/zip_set_name.c
+++ b/lib/zip_set_name.c
@@ -1,6 +1,6 @@
 /*
   zip_set_name.c -- rename helper function
-  Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -47,12 +47,12 @@
     zip_int64_t i;
 
     if (idx >= za->nentry) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
     if (ZIP_IS_RDONLY(za)) {
-	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
 	return -1;
     }
 
@@ -69,7 +69,7 @@
     /* TODO: encoding flags needed for CP437? */
     if ((i=_zip_name_locate(za, name, 0, NULL)) >= 0 && (zip_uint64_t)i != idx) {
 	_zip_string_free(str);
-	_zip_error_set(&za->error, ZIP_ER_EXISTS, 0);
+	zip_error_set(&za->error, ZIP_ER_EXISTS, 0);
 	return -1;
     }
 
@@ -95,7 +95,7 @@
     if (changed) {
         if (e->changes == NULL) {
             if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) {
-                _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+                zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
 		_zip_string_free(str);
                 return -1;
             }
diff --git a/lib/zip_source_begin_write.c b/lib/zip_source_begin_write.c
new file mode 100644
index 0000000..a6a8563
--- /dev/null
+++ b/lib/zip_source_begin_write.c
@@ -0,0 +1,53 @@
+/*
+  zip_source_begin_write.c -- start a new file for writing
+  Copyright (C) 2014 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>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include "zipint.h"
+
+
+int
+zip_source_begin_write(struct zip_source *src)
+{
+    if (ZIP_SOURCE_IS_OPEN_WRITING(src)) {
+        zip_error_set(&src->error, ZIP_ER_INVAL, 0);
+        return -1;
+    }
+    
+    if (_zip_source_call(src, NULL, 0, ZIP_SOURCE_BEGIN_WRITE) < 0) {
+        return -1;
+    }
+
+    src->write_state = ZIP_SOURCE_WRITE_OPEN;
+    
+    return 0;
+}
diff --git a/lib/zip_source_buffer.c b/lib/zip_source_buffer.c
index bc6a6cb..85631ad 100644
--- a/lib/zip_source_buffer.c
+++ b/lib/zip_source_buffer.c
@@ -1,6 +1,6 @@
 /*
   zip_source_buffer.c -- create zip data source from buffer
-  Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -37,41 +37,80 @@
 
 #include "zipint.h"
 
-struct read_data {
-    const char *buf, *data, *end;
-    time_t mtime;
-    int freep;
+#ifndef WRITE_FRAGMENT_SIZE
+#define WRITE_FRAGMENT_SIZE 64*1024
+#endif
+
+struct buffer {
+    zip_uint64_t fragment_size;		/* size of each fragment */
+
+    zip_uint8_t **fragments;		/* pointers to fragments */
+    zip_uint64_t nfragments;		/* number of allocated fragments */
+    zip_uint64_t fragments_capacity;	/* size of fragments (number of pointers) */
+    zip_uint64_t size;			/* size of data in bytes */
+    zip_uint64_t offset;		/* current offset */
+    int free_data;
 };
 
+typedef struct buffer buffer_t;
+
+struct read_data {
+    zip_error_t error;
+    time_t mtime;
+    buffer_t *in;
+    buffer_t *out;
+};
+
+static void buffer_free(buffer_t *buffer);
+static buffer_t *buffer_new(zip_uint64_t fragment_size);
+static buffer_t *buffer_new_read(const void *data, zip_uint64_t length, int free_data);
+static buffer_t *buffer_new_write(zip_uint64_t fragment_size);
+static zip_int64_t buffer_read(buffer_t *buffer, void *data, zip_uint64_t length);
+static int buffer_seek(buffer_t *buffer, void *data, zip_uint64_t len, zip_error_t *error);
+static zip_int64_t buffer_write(buffer_t *buffer, const void *data, zip_uint64_t length, zip_error_t *);
+
 static zip_int64_t read_data(void *, void *, zip_uint64_t, enum zip_source_cmd);
 
 
 ZIP_EXTERN struct zip_source *
 zip_source_buffer(struct zip *za, const void *data, zip_uint64_t len, int freep)
 {
-    struct read_data *f;
-    struct zip_source *zs;
-
     if (za == NULL)
 	return NULL;
 
+    return zip_source_buffer_create(data, len, freep, &za->error);
+}
+
+
+ZIP_EXTERN struct zip_source *
+zip_source_buffer_create(const void *data, zip_uint64_t len, int freep, zip_error_t *error)
+{
+    struct read_data *ctx;
+    struct zip_source *zs;
+
     if (data == NULL && len > 0) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(error, ZIP_ER_INVAL, 0);
 	return NULL;
     }
 
-    if ((f=(struct read_data *)malloc(sizeof(*f))) == NULL) {
-	_zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+    if ((ctx=(struct read_data *)malloc(sizeof(*ctx))) == NULL) {
+	zip_error_set(error, ZIP_ER_MEMORY, 0);
 	return NULL;
     }
 
-    f->data = (const char *)data;
-    f->end = ((const char *)data)+len;
-    f->freep = freep;
-    f->mtime = time(NULL);
+    if ((ctx->in = buffer_new_read(data, len, freep)) == NULL) {
+	zip_error_set(error, ZIP_ER_MEMORY, 0);
+	free(ctx);
+	return NULL;
+    }
+
+    ctx->out = NULL;
+    ctx->mtime = time(NULL);
+    zip_error_init(&ctx->error);
     
-    if ((zs=zip_source_function(za, read_data, f)) == NULL) {
-	free(f);
+    if ((zs=zip_source_function_create(read_data, ctx, error)) == NULL) {
+	buffer_free(ctx->in);
+	free(ctx);
 	return NULL;
     }
 
@@ -82,77 +121,316 @@
 static zip_int64_t
 read_data(void *state, void *data, zip_uint64_t len, enum zip_source_cmd cmd)
 {
-    struct read_data *z;
+    struct read_data *ctx = (struct read_data *)state;
     char *buf;
     zip_uint64_t n;
 
-    z = (struct read_data *)state;
-    buf = (char *)data;
-
     switch (cmd) {
-    case ZIP_SOURCE_OPEN:
-	z->buf = z->data;
-	return 0;
-	
-    case ZIP_SOURCE_READ:
-	n = (zip_uint64_t)(z->end - z->buf);
-	if (n > len)
-	    n = len;
+        case ZIP_SOURCE_BEGIN_WRITE:
+	    if ((ctx->out = buffer_new_write(WRITE_FRAGMENT_SIZE)) == NULL) {
+		zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
+		return -1;
+	    }
+	    return 0;
 
-	if (n) {
-	    memcpy(buf, z->buf, n);
-	    z->buf += n;
+        case ZIP_SOURCE_CLOSE:
+            return 0;
+            
+        case ZIP_SOURCE_COMMIT_WRITE:
+	    buffer_free(ctx->in);
+	    ctx->in = ctx->out;
+	    ctx->out = NULL;
+	    return 0;
+
+        case ZIP_SOURCE_ERROR:
+            return zip_error_to_data(&ctx->error, data, len);
+            
+        case ZIP_SOURCE_FREE:
+	    buffer_free(ctx->in);
+	    buffer_free(ctx->out);
+            free(ctx);
+            return 0;
+            
+        case ZIP_SOURCE_OPEN:
+	    ctx->in->offset = 0;
+            return 0;
+	
+        case ZIP_SOURCE_READ:
+            return buffer_read(ctx->in, data, len);
+	
+        case ZIP_SOURCE_REMOVE:
+	{
+	    buffer_t *empty = buffer_new_read(NULL, 0, 0);
+	    if (empty == 0) {
+		zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
+		return -1;
+	    }
+	
+	    buffer_free(ctx->in);
+	    ctx->in = empty;
+	    return 0;
 	}
 
-	return (zip_int64_t)n;
-	
-    case ZIP_SOURCE_CLOSE:
-	return 0;
+        case ZIP_SOURCE_ROLLBACK_WRITE:
+	    buffer_free(ctx->out);
+	    ctx->out = NULL;
+	    return 0;
 
-    case ZIP_SOURCE_STAT:
+        case ZIP_SOURCE_SEEK:
+	    return buffer_seek(ctx->in, data, len, &ctx->error);
+
+        case ZIP_SOURCE_SEEK_WRITE:
+	    return buffer_seek(ctx->out, data, len, &ctx->error);
+       
+        case ZIP_SOURCE_STAT:
         {
-	    struct zip_stat *st;
+            struct zip_stat *st;
 	    
-	    if (len < sizeof(*st))
+	    if (len < sizeof(*st)) {
+                zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
 		return -1;
+	    }
 
 	    st = (struct zip_stat *)data;
 
 	    zip_stat_init(st);
-	    st->mtime = z->mtime;
-	    st->size = (zip_uint64_t)(z->end - z->data);
+	    st->mtime = ctx->mtime;
+	    st->size = ctx->in->size;
 	    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|ZIP_STAT_COMP_SIZE
-		|ZIP_STAT_COMP_METHOD|ZIP_STAT_ENCRYPTION_METHOD;
+	    st->valid = ZIP_STAT_MTIME|ZIP_STAT_SIZE|ZIP_STAT_COMP_SIZE|ZIP_STAT_COMP_METHOD|ZIP_STAT_ENCRYPTION_METHOD;
 	    
 	    return sizeof(*st);
 	}
 
-    case ZIP_SOURCE_ERROR:
-	{
-	    int *e;
+        case ZIP_SOURCE_SUPPORTS:
+	    return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_SEEK, ZIP_SOURCE_TELL, ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_COMMIT_WRITE, ZIP_SOURCE_REMOVE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_WRITE, -1);
+            
+        case ZIP_SOURCE_TELL:
+            return ctx->in->offset;
+            
+        case ZIP_SOURCE_TELL_WRITE:
+            return ctx->out->offset;
 
-	    if (len < sizeof(int)*2)
-		return -1;
+        case ZIP_SOURCE_WRITE:
+	    return buffer_write(ctx->out, data, len, &ctx->error);
 
-	    e = (int *)data;
-	    e[0] = e[1] = 0;
-	}
-	return sizeof(int)*2;
+        default:
+	    zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
+            return -1;
+    }
+}
 
-    case ZIP_SOURCE_FREE:
-	if (z->freep) {
-	    free((void *)z->data);
-	    z->data = NULL;
-	}
-	free(z);
-	return 0;
 
-    default:
-	;
+static void
+buffer_free(buffer_t *buffer)
+{
+    if (buffer == NULL) {
+	return; 
     }
 
-    return -1;
+    if (buffer->free_data) {
+	zip_uint64_t i;
+
+	for (i=0; i < buffer->nfragments; i++) {
+	    free(buffer->fragments[i]);
+	}
+    }
+    free(buffer->fragments);
+    free(buffer);
+}
+
+
+static buffer_t *
+buffer_new(zip_uint64_t fragment_size)
+{
+    buffer_t *buffer;
+
+    if ((buffer = malloc(sizeof(*buffer))) == NULL) {
+	return NULL;
+    }
+
+    buffer->fragment_size = fragment_size;
+    buffer->offset = 0;
+    buffer->free_data = 0;
+    buffer->nfragments = 0;
+    buffer->fragments_capacity = 0;
+    buffer->fragments = NULL;
+    buffer->size = 0;
+
+    return buffer;
+}
+
+
+static buffer_t *
+buffer_new_read(const void *data, zip_uint64_t length, int free_data)
+{
+    buffer_t *buffer;
+
+    if ((buffer = buffer_new(length)) == NULL) {
+	return NULL;
+    }
+
+    buffer->size = length;
+
+    if (length > 0) {
+	if ((buffer->fragments = malloc(sizeof(*(buffer->fragments)))) == NULL) {
+	    buffer_free(buffer);
+	    return NULL;
+	}
+	buffer->fragments_capacity = 1;
+
+	buffer->nfragments = 1;
+	buffer->fragments[0] = (zip_uint8_t *)data;
+	buffer->free_data = free_data;
+    }
+
+    return buffer;
+}
+
+
+static buffer_t *
+buffer_new_write(zip_uint64_t fragment_size)
+{
+    buffer_t *buffer;
+
+    if ((buffer = buffer_new(fragment_size)) == NULL) {
+	return NULL;
+    }
+
+    if ((buffer->fragments = malloc(sizeof(*(buffer->fragments)))) == NULL) {
+	buffer_free(buffer);
+	return NULL;
+    }
+    buffer->fragments_capacity = 1;
+    buffer->nfragments = 0;
+    buffer->free_data = 1;
+
+    return buffer;
+}
+
+
+static zip_int64_t
+buffer_read(buffer_t *buffer, void *data, zip_uint64_t length)
+{
+    zip_uint64_t n, i, fragment_offset;
+
+    length = ZIP_MIN(length, buffer->size - buffer->offset);
+
+    if (length == 0) {
+	return 0;
+    }
+
+    i = buffer->offset / buffer->fragment_size;
+    fragment_offset = buffer->offset % buffer->fragment_size;
+    n = 0;
+    while (n < length) {
+	zip_uint64_t left = ZIP_MIN(length - n, buffer->fragment_size - fragment_offset);
+	
+	memcpy(data + n, buffer->fragments[i] + fragment_offset, left);
+
+	n += left;
+	i++;
+	fragment_offset = 0;
+    }
+
+    buffer->offset += n;
+    return n;
+}
+
+
+static int
+buffer_seek(buffer_t *buffer, void *data, zip_uint64_t len, zip_error_t *error)
+{
+    zip_uint64_t offset;
+    zip_source_args_seek_t *args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, error);
+
+    if (args == NULL)
+	return -1;
+            
+    switch (args->whence) {
+    case SEEK_CUR:
+	offset = buffer->offset + args->offset;
+	break;
+                    
+    case SEEK_END:
+	offset = buffer->size + args->offset;
+	break;
+
+    case SEEK_SET:
+	offset = args->offset;
+	break;
+    }
+
+    if (offset < 0 || (zip_uint64_t)offset > buffer->size) {
+	zip_error_set(error, ZIP_ER_INVAL, 0);
+	return -1;
+    }
+
+    buffer->offset = offset;
+    return 0;
+}
+
+
+static zip_int64_t
+buffer_write(buffer_t *buffer, const void *data, zip_uint64_t length, zip_error_t *error)
+{
+    zip_uint64_t n, i, fragment_offset;
+
+    if (buffer->offset + length + buffer->fragment_size - 1 < length) {
+	zip_error_set(error, ZIP_ER_INVAL, 0);
+	return -1;
+    }
+
+    /* grow buffer if needed */
+    if (buffer->offset + length > buffer->nfragments * buffer->fragment_size) {
+	zip_uint64_t needed_fragments = (buffer->offset + length + buffer->fragment_size - 1) / buffer->fragment_size;
+	
+	if (needed_fragments > buffer->fragments_capacity) {
+	    zip_uint64_t new_capacity = buffer->fragments_capacity;
+
+	    while (new_capacity < needed_fragments) {
+		new_capacity *= 2;
+	    }
+
+	    zip_uint8_t **fragments = realloc(buffer->fragments, new_capacity * sizeof(*fragments));
+
+	    if (fragments == NULL) {
+		zip_error_set(error, ZIP_ER_MEMORY, 0);
+		return -1;
+	    }
+
+	    buffer->fragments = fragments;
+	    buffer->fragments_capacity = new_capacity;
+	}
+
+	while (buffer->nfragments < needed_fragments) {
+	    if ((buffer->fragments[buffer->nfragments] = malloc(buffer->fragment_size)) == NULL) {
+		zip_error_set(error, ZIP_ER_MEMORY, 0);
+		return -1;
+	    }
+	    buffer->nfragments++;
+	}
+    }
+
+    i = buffer->offset / buffer->fragment_size;
+    fragment_offset = buffer->offset % buffer->fragment_size;
+    n = 0;
+    while (n < length) {
+	zip_uint64_t left = ZIP_MIN(length - n, buffer->fragment_size - fragment_offset);
+		
+	memcpy(buffer->fragments[i] + fragment_offset, data + n, left);
+
+	n += left;
+	i++;
+	fragment_offset = 0;
+    }
+
+    buffer->offset += n;
+    if (buffer->offset > buffer->size) {
+	buffer->size = buffer->offset;
+    }
+
+    return n;
 }
diff --git a/lib/zip_source_call.c b/lib/zip_source_call.c
new file mode 100644
index 0000000..e0a1a63
--- /dev/null
+++ b/lib/zip_source_call.c
@@ -0,0 +1,64 @@
+/*
+ zip_source_call.c -- invoke callback command on zip_source
+ Copyright (C) 2009-2014 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>
+ 
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ 3. The names of the authors may not be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+ 
+ THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "zipint.h"
+
+
+zip_int64_t
+_zip_source_call(zip_source_t *src, void *data, zip_uint64_t length, enum zip_source_cmd command)
+{
+    zip_int64_t ret;
+
+    if (src->src == NULL) {
+        ret = src->cb.f(src->ud, data, length, command);
+    }
+    else {
+        ret = src->cb.l(src->src, src->ud, data, length, command);
+    }
+    
+    if (ret < 0) {
+        if (command != ZIP_SOURCE_ERROR && command != ZIP_SOURCE_SUPPORTS) {
+            int e[2];
+            
+            if (_zip_source_call(src, e, sizeof(e), ZIP_SOURCE_ERROR) < 0) {
+                zip_error_set(&src->error, ZIP_ER_INTERNAL, 0);
+            }
+            else {
+                zip_error_set(&src->error, e[0], e[1]);
+            }
+        }
+    }
+
+    return ret;
+}
diff --git a/lib/zip_source_close.c b/lib/zip_source_close.c
index 0075503..cbc69c4 100644
--- a/lib/zip_source_close.c
+++ b/lib/zip_source_close.c
@@ -1,6 +1,6 @@
 /*
   zip_source_close.c -- close zip_source (stop reading)
-  Copyright (C) 2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 2009-2014 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>
@@ -35,18 +35,24 @@
 #include "zipint.h"
 
 
-void
+int
 zip_source_close(struct zip_source *src)
 {
-    if (!src->is_open)
-	return;
-
-    if (src->src == NULL)
-	(void)src->cb.f(src->ud, NULL, 0, ZIP_SOURCE_CLOSE);
-    else {
-	(void)src->cb.l(src->src, src->ud, NULL, 0, ZIP_SOURCE_CLOSE);
-	zip_source_close(src->src);
+    if (!ZIP_SOURCE_IS_OPEN_READING(src)) {
+        zip_error_set(&src->error, ZIP_ER_INVAL, 0);
+        return -1;
     }
     
-    src->is_open = 0;
+    src->open_count--;
+    if (src->open_count == 0) {
+	_zip_source_call(src, NULL, 0, ZIP_SOURCE_CLOSE);
+    }
+
+    if (ZIP_SOURCE_IS_LAYERED(src)) {
+	if (zip_source_close(src->src) < 0) {
+	    zip_error_set(&src->error, ZIP_ER_INTERNAL, 0);
+	}
+    }
+    
+    return 0;
 }
diff --git a/lib/zip_source_commit_write.c b/lib/zip_source_commit_write.c
new file mode 100644
index 0000000..09fe3da
--- /dev/null
+++ b/lib/zip_source_commit_write.c
@@ -0,0 +1,64 @@
+/*
+  zip_source_commit_write.c -- commit changes to file
+  Copyright (C) 2014 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>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include "zipint.h"
+
+
+int
+zip_source_commit_write(struct zip_source *src)
+{
+    if (!ZIP_SOURCE_IS_OPEN_WRITING(src)) {
+        zip_error_set(&src->error, ZIP_ER_INVAL, 0);
+        return -1;
+    }
+    
+    if (src->open_count > 1) {
+	zip_error_set(&src->error, ZIP_ER_INUSE, 0);
+	return -1;
+    }
+    else if (ZIP_SOURCE_IS_OPEN_READING(src)) {
+        if (zip_source_close(src) < 0) {
+	    return -1;
+	}
+    }
+    
+    if (_zip_source_call(src, NULL, 0, ZIP_SOURCE_COMMIT_WRITE) < 0) {
+        src->write_state = ZIP_SOURCE_WRITE_FAILED;
+        return -1;
+    }
+    
+    src->write_state = ZIP_SOURCE_WRITE_CLOSED;
+    
+    return 0;
+}
diff --git a/lib/zip_source_crc.c b/lib/zip_source_crc.c
index fda2caa..f3d5048 100644
--- a/lib/zip_source_crc.c
+++ b/lib/zip_source_crc.c
@@ -1,6 +1,6 @@
 /*
   zip_source_crc.c -- pass-through source that calculates CRC32 and size
-  Copyright (C) 2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 2009-2014 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>
@@ -34,19 +34,19 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <limits.h>
 
 #include "zipint.h"
 
 struct crc_context {
     int eof;
     int validate;
-    int e[2];
+    zip_error_t error;
     zip_uint64_t size;
     zip_uint32_t crc;
 };
 
-static zip_int64_t crc_read(struct zip_source *, 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);
 
 
 struct zip_source *
@@ -55,18 +55,18 @@
     struct crc_context *ctx;
 
     if (src == NULL) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return NULL;
     }
 
     if ((ctx=(struct crc_context *)malloc(sizeof(*ctx))) == NULL) {
-	_zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+	zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
 	return NULL;
     }
 
     ctx->eof = 0;
     ctx->validate = validate;
-    ctx->e[0] = ctx->e[1] = 0;
+    zip_error_init(&ctx->error);
     ctx->size = 0;
     ctx->crc = 0;
     
@@ -75,8 +75,7 @@
 
 
 static zip_int64_t
-crc_read(struct zip_source *src, 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_context *ctx;
     zip_int64_t n;
@@ -84,54 +83,60 @@
     ctx = (struct crc_context *)_ctx;
 
     switch (cmd) {
-    case ZIP_SOURCE_OPEN:
-	ctx->eof = 0;
-	ctx->crc = (zip_uint32_t)crc32(0, NULL, 0);
-	ctx->size = 0;
+        case ZIP_SOURCE_OPEN:
+            ctx->eof = 0;
+            ctx->crc = (zip_uint32_t)crc32(0, NULL, 0);
+            ctx->size = 0;
+            
+            return 0;
+            
+        case ZIP_SOURCE_READ:
+            if (ctx->eof || len == 0)
+                return 0;
+            
+            if ((n=zip_source_read(src, data, len)) < 0) {
+                zip_error_set_from_source(&ctx->error, src);
+                return -1;
+            }
+            
+            if (n == 0) {
+                ctx->eof = 1;
+                if (ctx->validate) {
+                    struct zip_stat st;
+                    
+                    if (zip_source_stat(src, &st) < 0) {
+                        zip_error_set_from_source(&ctx->error, src);
+                        return -1;
+                    }
+                    
+                    if ((st.valid & ZIP_STAT_CRC) && st.crc != ctx->crc) {
+                        zip_error_set(&ctx->error, ZIP_ER_CRC, 0);
+                        return -1;
+                    }
+                    if ((st.valid & ZIP_STAT_SIZE) && st.size != ctx->size) {
+                        zip_error_set(&ctx->error, ZIP_ER_INCONS, 0);
+                        return -1;
+                    }
+                }
+            }
+            else {
+		zip_uint64_t i, nn;
 
-	return 0;
+		for (i=0; i < n; i += nn) {
+		    nn = ZIP_MIN(UINT_MAX, n-i);
 
-    case ZIP_SOURCE_READ:
-	if (ctx->eof || len == 0)
-	    return 0;
-
-	if ((n=zip_source_read(src, data, len)) < 0)
-	    return ZIP_SOURCE_ERR_LOWER;
-
-	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;
+		    ctx->crc = (zip_uint32_t)crc32(ctx->crc, (const Bytef *)data+i, (uInt)nn);
 		}
-		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 += (zip_uint64_t)n;
-	    ctx->crc = (zip_uint32_t)crc32(ctx->crc, (const Bytef *)data, (uInt)n); /* TODO: check for overflow, use multiple crc calls if needed */
-	}
-	return n;
+                ctx->size += (zip_uint64_t)n;
+            }
+            return n;
 
-    case ZIP_SOURCE_CLOSE:
-	return 0;
+        case ZIP_SOURCE_CLOSE:
+            return 0;
 
-    case ZIP_SOURCE_STAT:
-	{
-	    struct zip_stat *st;
+        case ZIP_SOURCE_STAT:
+        {
+            struct zip_stat *st;
 
 	    st = (struct zip_stat *)data;
 
@@ -145,19 +150,21 @@
 		st->encryption_method = ZIP_EM_NONE;
 		st->valid |= ZIP_STAT_SIZE|ZIP_STAT_CRC|ZIP_STAT_COMP_SIZE|ZIP_STAT_COMP_METHOD|ZIP_STAT_ENCRYPTION_METHOD;;
 	    }
-	}
-	return 0;
-	
-    case ZIP_SOURCE_ERROR:
-	memcpy(data, ctx->e, sizeof(ctx->e));
-	return 0;
+            return 0;
+        }
+            
+        case ZIP_SOURCE_ERROR:
+            return zip_error_to_data(&ctx->error, data, len);
 
-    case ZIP_SOURCE_FREE:
-	free(ctx);
-	return 0;
-
-    default:
-	return -1;
+        case ZIP_SOURCE_FREE:
+            free(ctx);
+            return 0;
+            
+        case ZIP_SOURCE_SUPPORTS:
+            return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, -1);
+            
+        default:
+            zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
+            return -1;
     }
-    
 }
diff --git a/lib/zip_source_deflate.c b/lib/zip_source_deflate.c
index 4950d4e..344ccd9 100644
--- a/lib/zip_source_deflate.c
+++ b/lib/zip_source_deflate.c
@@ -1,6 +1,6 @@
 /*
   zip_source_deflate.c -- deflate (de)compressoin routines
-  Copyright (C) 2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 2009-2014 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>
@@ -34,12 +34,13 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <limits.h>
 
 #include "zipint.h"
 
 struct deflate {
-    int e[2];
-
+    zip_error_t error;
+    
     int eof;
     int mem_level;
     zip_uint64_t size;
@@ -47,35 +48,30 @@
     z_stream zstr;
 };
 
-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 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 *);
 
 
 struct zip_source *
-zip_source_deflate(struct zip *za, struct zip_source *src,
-		   zip_int32_t cm, int flags)
+zip_source_deflate(struct zip *za, struct zip_source *src, zip_int32_t cm, int flags)
 {
     struct deflate *ctx;
     struct zip_source *s2;
 
     if (src == NULL || (cm != ZIP_CM_DEFLATE && !ZIP_CM_IS_DEFAULT(cm))) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return NULL;
     }
 
     if ((ctx=(struct deflate *)malloc(sizeof(*ctx))) == NULL) {
-	_zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+	zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
 	return NULL;
     }
 
-    ctx->e[0] = ctx->e[1] = 0;
+    zip_error_init(&ctx->error);
     ctx->eof = 0;
     if (flags & ZIP_CODEC_ENCODE) {
 	if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0))
@@ -97,20 +93,23 @@
 
 
 static zip_int64_t
-compress_read(struct zip_source *src, 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;
+    zip_uint64_t out_offset;
+    uInt out_len;
 
-    if (ctx->e[0] != 0)
+    if (zip_error_code_zip(&ctx->error) != ZIP_ER_OK)
 	return -1;
     
     if (len == 0)
 	return 0;
 	
+    out_offset = 0;
+    out_len = (uInt)ZIP_MIN(UINT_MAX, len);
     ctx->zstr.next_out = (Bytef *)data;
-    ctx->zstr.avail_out = (uInt)len; /* TODO: check for overflow */
+    ctx->zstr.avail_out = out_len;
 
     end = 0;
     while (!end) {
@@ -121,8 +120,18 @@
 	case Z_STREAM_END:
 	    /* all ok */
 
-	    if (ctx->zstr.avail_out == 0
-		|| (ctx->eof && ctx->zstr.avail_in == 0))
+	    if (ctx->zstr.avail_out == 0) {
+		out_offset += out_len;
+		if (out_offset < len) {
+		    out_len = (uInt)ZIP_MIN(UINT_MAX, len-out_offset);
+		    ctx->zstr.next_out = (Bytef *)data+out_offset;
+		    ctx->zstr.avail_out = out_len;
+		}
+		else {
+		    end = 1;
+		}
+	    }
+	    else if (ctx->eof && ctx->zstr.avail_in == 0)
 		end = 1;
 	    break;
 
@@ -134,7 +143,7 @@
 		}
 
 		if ((n=zip_source_read(src, ctx->buffer, sizeof(ctx->buffer))) < 0) {
-		    zip_source_error(src, ctx->e, ctx->e+1);
+                    zip_error_set_from_source(&ctx->error, src);
 		    end = 1;
 		    break;
 		}
@@ -154,8 +163,7 @@
 	case Z_DATA_ERROR:
 	case Z_STREAM_ERROR:
 	case Z_MEM_ERROR:
-	    ctx->e[0] = ZIP_ER_ZLIB;
-	    ctx->e[1] = ret;
+            zip_error_set(&ctx->error, ZIP_ER_ZLIB, ret);
 
 	    end = 1;
 	    break;
@@ -165,32 +173,46 @@
     if (ctx->zstr.avail_out < len)
 	return (zip_int64_t)(len - ctx->zstr.avail_out);
 
-    return (ctx->e[0] == 0) ? 0 : -1;
+    return (zip_error_code_zip(&ctx->error) == ZIP_ER_OK) ? 0 : -1;
 }
 
 
 static zip_int64_t
-decompress_read(struct zip_source *src, 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;
+    zip_uint64_t out_offset;
+    uInt out_len;
 
-    if (ctx->e[0] != 0)
+    if (zip_error_code_zip(&ctx->error) != ZIP_ER_OK)
 	return -1;
     
     if (len == 0)
 	return 0;
-	
+
+    out_offset = 0;
+    out_len = ZIP_MIN(UINT_MAX, len);
     ctx->zstr.next_out = (Bytef *)data;
-    ctx->zstr.avail_out = (uInt)len; /* TODO: check for overflow */
+    ctx->zstr.avail_out = out_len;
 
     end = 0;
-    while (!end && ctx->zstr.avail_out) {
+    while (!end) {
 	ret = inflate(&ctx->zstr, Z_SYNC_FLUSH);
 
 	switch (ret) {
 	case Z_OK:
+	    if (ctx->zstr.avail_out == 0) {
+		out_offset += out_len;
+		if (out_offset < len) {
+		    out_len = (uInt)ZIP_MIN(UINT_MAX, len-out_offset);
+		    ctx->zstr.next_out = (Bytef *)data+out_offset;
+		    ctx->zstr.avail_out = out_len;
+		}
+		else {
+		    end = 1;
+		}
+	    }
 	    break;
 	    
 	case Z_STREAM_END:
@@ -206,7 +228,7 @@
 		}
 
 		if ((n=zip_source_read(src, ctx->buffer, sizeof(ctx->buffer))) < 0) {
-		    zip_source_error(src, ctx->e, ctx->e+1);
+                    zip_error_set_from_source(&ctx->error, src);
 		    end = 1;
 		    break;
 		}
@@ -223,8 +245,7 @@
 	case Z_DATA_ERROR:
 	case Z_STREAM_ERROR:
 	case Z_MEM_ERROR:
-	    ctx->e[0] = ZIP_ER_ZLIB;
-	    ctx->e[1] = ret;
+            zip_error_set(&ctx->error, ZIP_ER_ZLIB, ret);
 	    end = 1;
 	    break;
 	}
@@ -233,13 +254,12 @@
     if (ctx->zstr.avail_out < len)
 	return (zip_int64_t)(len - ctx->zstr.avail_out);
 
-    return (ctx->e[0] == 0) ? 0 : -1;
+    return (zip_error_code_zip(&ctx->error) == ZIP_ER_OK) ? 0 : -1;
 }
 
 
 static zip_int64_t
-deflate_compress(struct zip_source *src, 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;
@@ -257,11 +277,8 @@
 	ctx->zstr.next_out = NULL;
 
 	/* negative value to tell zlib not to write a header */
-	if ((ret=deflateInit2(&ctx->zstr, Z_BEST_COMPRESSION, Z_DEFLATED,
-			      -MAX_WBITS, ctx->mem_level,
-			      Z_DEFAULT_STRATEGY)) != Z_OK) {
-	    ctx->e[0] = ZIP_ER_ZLIB;
-	    ctx->e[1] = ret;
+	if ((ret=deflateInit2(&ctx->zstr, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, ctx->mem_level, Z_DEFAULT_STRATEGY)) != Z_OK) {
+            zip_error_set(&ctx->error, ZIP_ER_ZLIB, ret);
 	    return -1;
 	}
 
@@ -292,16 +309,14 @@
 	return 0;
 
     case ZIP_SOURCE_ERROR:
-	memcpy(data, ctx->e, sizeof(int)*2);
-	return sizeof(int)*2;
+        return zip_error_to_data(&ctx->error, data, len);
 
     case ZIP_SOURCE_FREE:
 	deflate_free(ctx);
 	return 0;
 
     default:
-	ctx->e[0] = ZIP_ER_INVAL;
-	ctx->e[1] = 0;
+        zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0);
 	return -1;
     }
 }
@@ -318,68 +333,64 @@
     ctx = (struct deflate *)ud;
 
     switch (cmd) {
-    case ZIP_SOURCE_OPEN:
-	if ((n=zip_source_read(src, ctx->buffer, sizeof(ctx->buffer))) < 0)
-	    return ZIP_SOURCE_ERR_LOWER;
+        case ZIP_SOURCE_OPEN:
+            if ((n=zip_source_read(src, ctx->buffer, sizeof(ctx->buffer))) < 0) {
+                zip_error_set_from_source(&ctx->error, src);
+                return -1;
+            }
 
-	ctx->zstr.zalloc = Z_NULL;
-	ctx->zstr.zfree = Z_NULL;
-	ctx->zstr.opaque = NULL;
-	ctx->zstr.next_in = (Bytef *)ctx->buffer;
-	ctx->zstr.avail_in = (uInt)n /* TODO: check for overflow */;
+            ctx->zstr.zalloc = Z_NULL;
+            ctx->zstr.zfree = Z_NULL;
+            ctx->zstr.opaque = NULL;
+            ctx->zstr.next_in = (Bytef *)ctx->buffer;
+            ctx->zstr.avail_in = (uInt)n;
 
-	/* negative value to tell zlib that there is no header */
-	if ((ret=inflateInit2(&ctx->zstr, -MAX_WBITS)) != Z_OK) {
-	    ctx->e[0] = ZIP_ER_ZLIB;
-	    ctx->e[1] = ret;
+            /* negative value to tell zlib that there is no header */
+            if ((ret=inflateInit2(&ctx->zstr, -MAX_WBITS)) != Z_OK) {
+                zip_error_set(&ctx->error, ZIP_ER_ZLIB, ret);
+                return -1;
+            }
+            return 0;
 
-	    return -1;
-	}
-	return 0;
+        case ZIP_SOURCE_READ:
+            return decompress_read(src, ctx, data, len);
 
-    case ZIP_SOURCE_READ:
-	return decompress_read(src, ctx, data, len);
+        case ZIP_SOURCE_CLOSE:
+            inflateEnd(&ctx->zstr);
+            return 0;
 
-    case ZIP_SOURCE_CLOSE:
-	inflateEnd(&ctx->zstr);
-	return 0;
+        case ZIP_SOURCE_STAT:
+        {
+            struct zip_stat *st;
+            
+            st = (struct zip_stat *)data;
+            
+            st->comp_method = ZIP_CM_STORE;
+            if (st->comp_size > 0 && st->size > 0)
+                st->comp_size = st->size;
+            
+            return 0;
+        }
 
-    case ZIP_SOURCE_STAT:
-	{
-	    struct zip_stat *st;
+        case ZIP_SOURCE_ERROR:
+            return zip_error_to_data(&ctx->error, data, len);
 
-	    st = (struct zip_stat *)data;
+        case ZIP_SOURCE_FREE:
+            free(ctx);
+            return 0;
+            
+        case ZIP_SOURCE_SUPPORTS:
+            return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, -1);
 
-	    st->comp_method = ZIP_CM_STORE;
-	    if (st->comp_size > 0 && st->size > 0)
-		st->comp_size = st->size;
-	}
-	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:
-	/* TODO: inflateEnd if close was not called */
-	free(ctx);
-	return 0;
-
-    default:
-	ctx->e[0] = ZIP_ER_INVAL;
-	ctx->e[1] = 0;
-	return -1;
+        default:
+            zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
+            return -1;
     }
-    
 }
 
 
 static void
 deflate_free(struct deflate *ctx)
 {
-    /* TODO: deflateEnd if close was not called */
     free(ctx);
 }
diff --git a/lib/zip_source_error.c b/lib/zip_source_error.c
index 4999491..401a32e 100644
--- a/lib/zip_source_error.c
+++ b/lib/zip_source_error.c
@@ -1,6 +1,6 @@
 /*
   zip_source_error.c -- get last error from zip_source
-  Copyright (C) 2009-2013 Dieter Baron and Thomas Klausner
+  Copyright (C) 2009-2014 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>
@@ -35,48 +35,8 @@
 #include "zipint.h"
 
 
-void
-zip_source_error(struct zip_source *src, int *ze, int *se)
+struct zip_error *
+zip_source_error(struct zip_source *src)
 {
-    int e[2];
-
-    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 {
-	switch (src->error_source) {
-	case ZIP_LES_NONE:
-	    e[0] = e[1] = 0;
-	    break;
-
-	case ZIP_LES_INVAL:
-	    e[0] = ZIP_ER_INVAL;
-	    e[1] = 0;
-	    break;
-
-	case ZIP_LES_LOWER:
-	    zip_source_error(src->src, ze, se);
-	    return;
-
-	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;
-	    break;
-	}
-    }
-
-    if (ze)
-	*ze = e[0];
-    if (se)
-	*se = e[1];
+    return &src->error;
 }
diff --git a/lib/zip_source_file.c b/lib/zip_source_file.c
index 4a6824a..0fddfbd 100644
--- a/lib/zip_source_file.c
+++ b/lib/zip_source_file.c
@@ -1,6 +1,6 @@
 /*
   zip_source_file.c -- create data source from file
-  Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -39,16 +39,22 @@
 
 
 ZIP_EXTERN struct zip_source *
-zip_source_file(struct zip *za, const char *fname, zip_uint64_t start,
-		zip_int64_t len)
+zip_source_file(struct zip *za, const char *fname, zip_uint64_t start, zip_int64_t len)
 {
     if (za == NULL)
 	return NULL;
+    
+    return zip_source_file_create(fname, start, len, &za->error);
+}
 
-    if (fname == NULL || len < -1) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+
+ZIP_EXTERN zip_source_t *
+zip_source_file_create(const char *fname, zip_uint64_t start, zip_int64_t length, zip_error_t *error)
+{
+    if (fname == NULL || length < -1) {
+	zip_error_set(error, ZIP_ER_INVAL, 0);
 	return NULL;
     }
 
-    return _zip_source_file_or_p(za, fname, NULL, start, len, 1, NULL);
+    return _zip_source_file_or_p(fname, NULL, start, length, 1, NULL, error);
 }
diff --git a/lib/zip_source_filep.c b/lib/zip_source_filep.c
index b05e960..9fabf81 100644
--- a/lib/zip_source_filep.c
+++ b/lib/zip_source_filep.c
@@ -41,79 +41,105 @@
 #include "zipint.h"
 
 struct read_file {
-    char *fname;	/* name of file to copy from */
-    FILE *f;		/* file to copy from */
-    int closep;		/* close f */
-    struct zip_stat st;	/* stat information passed in */
+    zip_error_t error;      /* last error information */
+    zip_int64_t supports;
 
-    zip_uint64_t off;	/* start offset of */
-    zip_int64_t len;	/* length of data to copy */
-    zip_int64_t remain;	/* bytes remaining to be copied */
-    int e[2];		/* error codes */
-    int source_closed;  /* set if source archive is closed */
-    struct zip *source_archive;  /* zip archive we're reading from, NULL if not from archive */
+    /* reading */
+    char *fname;            /* name of file to read from */
+    FILE *f;                /* file to read from */
+    int closep;             /* whether to close f on ZIP_CMD_FREE */
+    struct zip_stat st;     /* stat information passed in */
+    zip_uint64_t start;     /* start offset of data to read */
+    zip_uint64_t end;       /* end offset of data to read, 0 for up to EOF */
+    zip_uint64_t current;   /* current offset */
+    
+    /* writing */
+    char *tmpname;
+    FILE *fout;
 };
 
 static zip_int64_t read_file(void *state, void *data, zip_uint64_t len, enum zip_source_cmd cmd);
-static void _zip_deregister_source(struct zip *za, void *ud);
-static int _zip_register_source(struct zip *za, struct zip_source *src);
+static int create_temp_output(struct read_file *ctx);
 
 
 ZIP_EXTERN struct zip_source *
-zip_source_filep(struct zip *za, FILE *file, zip_uint64_t start,
-		 zip_int64_t len)
+zip_source_filep(struct zip *za, FILE *file, zip_uint64_t start, zip_int64_t len)
 {
     if (za == NULL)
 	return NULL;
+    
+    return zip_source_filep_create(file, start, len, &za->error);
+}
 
-    if (file == NULL || len < -1) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+
+ZIP_EXTERN zip_source_t *
+zip_source_filep_create(FILE *file, zip_uint64_t start, zip_int64_t length, zip_error_t *error)
+{
+    if (file == NULL || length < -1) {
+	zip_error_set(error, ZIP_ER_INVAL, 0);
 	return NULL;
     }
 
-    return _zip_source_file_or_p(za, NULL, file, start, len, 1, NULL);
+    return _zip_source_file_or_p(NULL, file, start, length, 1, NULL, error);
 }
 
 
 struct zip_source *
-_zip_source_file_or_p(struct zip *za, const char *fname, FILE *file,
-		      zip_uint64_t start, zip_int64_t len, int closep,
-		      const struct zip_stat *st)
+_zip_source_file_or_p(const char *fname, FILE *file, zip_uint64_t start, zip_int64_t len, int closep, const zip_stat_t *st, zip_error_t *error)
 {
-    struct read_file *f;
+    struct read_file *ctx;
     struct zip_source *zs;
-
+    
     if (file == NULL && fname == NULL) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(error, ZIP_ER_INVAL, 0);
+	return NULL;
+    }
+    
+    if ((ctx=(struct read_file *)malloc(sizeof(struct read_file))) == NULL) {
+	zip_error_set(error, ZIP_ER_MEMORY, 0);
 	return NULL;
     }
 
-    if ((f=(struct read_file *)malloc(sizeof(struct read_file))) == NULL) {
-	_zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
-	return NULL;
-    }
-
-    f->fname = NULL;
+    ctx->fname = NULL;
     if (fname) {
-	if ((f->fname=strdup(fname)) == NULL) {
-	    _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
-	    free(f);
+	if ((ctx->fname=strdup(fname)) == NULL) {
+	    zip_error_set(error, ZIP_ER_MEMORY, 0);
+	    free(ctx);
 	    return NULL;
 	}
     }
-    f->f = file;
-    f->off = start;
-    f->len = (len ? len : -1);
-    f->closep = f->fname ? 1 : closep;
-    f->source_closed = 0;
-    f->source_archive = NULL;
-    if (st)
-	memcpy(&f->st, st, sizeof(f->st));
-    else
-	zip_stat_init(&f->st);
+    ctx->f = file;
+    ctx->start = start;
+    ctx->end = (len < 0 ? 0 : start+(zip_uint64_t)len);
+    ctx->closep = ctx->fname ? 1 : closep;
+    if (st) {
+	memcpy(&ctx->st, st, sizeof(ctx->st));
+        ctx->st.name = NULL;
+        ctx->st.valid &= ~ZIP_STAT_NAME;
+    }
+    else {
+	zip_stat_init(&ctx->st);
+    }
+    
+    ctx->tmpname = NULL;
+    ctx->fout = NULL;
+   
+    zip_error_init(&ctx->error);
 
-    if ((zs=zip_source_function(za, read_file, f)) == NULL) {
-	free(f);
+    ctx->supports = zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_TELL, -1);
+    if (ctx->fname) {
+	struct stat sb;
+
+	if (stat(ctx->fname, &sb) < 0 || S_ISREG(sb.st_mode)) {
+	    ctx->supports |= zip_source_make_command_bitmap(ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_COMMIT_WRITE, ZIP_SOURCE_REMOVE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_WRITE, -1);
+	}
+    }
+    else if (fseek(ctx->f, 0, SEEK_CUR) == 0) {
+	ctx->supports |= zip_source_make_command_bitmap(ZIP_SOURCE_SEEK, -1);
+    }
+
+    if ((zs=zip_source_function_create(read_file, ctx, error)) == NULL) {
+	free(ctx);
 	return NULL;
     }
 
@@ -121,132 +147,257 @@
 }
 
 
-int
-_zip_source_filep_set_source_archive(struct zip_source *src, struct zip *za)
+static int
+create_temp_output(struct read_file *ctx)
 {
-    struct read_file *z = (struct read_file *)src->ud;
-
-    z->source_archive = za;
-    return _zip_register_source(za, src);
-}
-
-
-/* called by zip_discard to avoid operating on file from closed archive */
-void
-_zip_source_filep_invalidate(struct zip_source *src)
-{
-    struct read_file *z = (struct read_file *)src->ud;
-
-    z->source_closed = 1;
-    z->f = NULL;
-    if (z->e[0] == ZIP_ER_OK) {
-	z->e[0] = ZIP_ER_ZIPCLOSED;
-	z->e[1] = 0;
+    char *temp;
+    int tfd;
+    FILE *tfp;
+    
+    if ((temp=(char *)malloc(strlen(ctx->fname)+8)) == NULL) {
+	zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
+	return -1;
     }
+    sprintf(temp, "%s.XXXXXX", ctx->fname);
+    
+    if ((tfd=mkstemp(temp)) == -1) {
+        zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
+        free(temp);
+        return -1;
+    }
+    
+    if ((tfp=fdopen(tfd, "r+b")) == NULL) {
+        zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
+        close(tfd);
+        (void)remove(temp);
+        free(temp);
+        return -1;
+    }
+    
+#ifdef _WIN32
+    /*
+     According to Pierre Joye, Windows in some environments per
+     default creates text files, so force binary mode.
+     */
+    _setmode(_fileno(tfp), _O_BINARY );
+#endif
+    
+    ctx->fout = tfp;
+    ctx->tmpname = temp;
+    
+    return 0;
 }
 
 
 static zip_int64_t
-read_file(void *state, void *data, zip_uint64_t len, enum zip_source_cmd cmd)
+read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd)
 {
-    struct read_file *z;
+    struct read_file *ctx;
     char *buf;
     zip_uint64_t n;
     size_t i;
 
-    z = (struct read_file *)state;
+    ctx = (struct read_file *)state;
     buf = (char *)data;
 
     switch (cmd) {
-    case ZIP_SOURCE_OPEN:
-	if (z->source_closed)
-	    return -1;
-
-	if (z->fname) {
-	    if ((z->f=fopen(z->fname, "rb")) == NULL) {
-		z->e[0] = ZIP_ER_OPEN;
-		z->e[1] = errno;
-		return -1;
-	    }
-	}
-
-	if (z->closep && z->off > 0) {
-	    if (fseeko(z->f, (off_t)z->off, SEEK_SET) < 0) {
-		z->e[0] = ZIP_ER_SEEK;
-		z->e[1] = errno;
-		return -1;
-	    }
-	}
-	z->remain = z->len;
-	return 0;
-	
-    case ZIP_SOURCE_READ:
-	if (z->source_closed)
-	    return -1;
-
-	if (z->remain != -1)
-	    n = len > (zip_uint64_t)z->remain ? (zip_uint64_t)z->remain : len;
-	else
-	    n = len;
-            
-        if (n > SIZE_MAX)
-            n = SIZE_MAX;
-
-	if (!z->closep) {
-	    /* we might share this file with others, so let's be safe */
-	    if (fseeko(z->f, (off_t)(z->off + (zip_uint64_t)(z->len-z->remain)), SEEK_SET) < 0) {
-		z->e[0] = ZIP_ER_SEEK;
-		z->e[1] = errno;
-		return -1;
-	    }
-	}
-
-	if ((i=fread(buf, 1, (size_t)n, z->f)) == 0) {
-            if (ferror(z->f)) {
-                z->e[0] = ZIP_ER_READ;
-                z->e[1] = errno;
+        case ZIP_SOURCE_BEGIN_WRITE:
+            if (ctx->fname == NULL) {
+                zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
                 return -1;
             }
+            return create_temp_output(ctx);
+            
+        case ZIP_SOURCE_COMMIT_WRITE: {
+	    mode_t mask;
+
+            if (fclose(ctx->fout) < 0) {
+                ctx->fout = NULL;
+                zip_error_set(&ctx->error, ZIP_ER_WRITE, errno);
+            }
+            ctx->fout = NULL;
+            if (rename(ctx->tmpname, ctx->fname) < 0) {
+                zip_error_set(&ctx->error, ZIP_ER_RENAME, errno);
+                return -1;
+            }
+	    mask = umask(022);
+	    umask(mask);
+	    chmod(ctx->fname, 0666&~mask);
+            return 0;
 	}
+            
+        case ZIP_SOURCE_CLOSE:
+            if (ctx->fname) {
+                fclose(ctx->f);
+                ctx->f = NULL;
+            }
+            return 0;
+            
+        case ZIP_SOURCE_ERROR:
+            return zip_error_to_data(&ctx->error, data, len);
+            
+        case ZIP_SOURCE_FREE:
+            free(ctx->fname);
+            if (ctx->closep && ctx->f)
+                fclose(ctx->f);
+            free(ctx);
+            return 0;
+            
+        case ZIP_SOURCE_OPEN:
+            if (ctx->fname) {
+                if ((ctx->f=fopen(ctx->fname, "rb")) == NULL) {
+                    zip_error_set(&ctx->error, ZIP_ER_OPEN, errno);
+                    return -1;
+                }
+            }
+            
+            if (ctx->closep && ctx->start > 0) {
+                if (fseeko(ctx->f, (off_t)ctx->start, SEEK_SET) < 0) {
+                    zip_error_set(&ctx->error, ZIP_ER_SEEK, errno);
+                    return -1;
+                }
+            }
+            ctx->current = ctx->start;
+            return 0;
+            
+        case ZIP_SOURCE_READ:
+            if (ctx->end > 0) {
+                n = ctx->end-ctx->current;
+                if (n > len) {
+                    n = len;
+                }
+            }
+            else {
+                n = len;
+            }
+            
+            if (n > SIZE_MAX)
+                n = SIZE_MAX;
 
-	if (z->remain != -1)
-	    z->remain -= i;
+            if (!ctx->closep) {
+                if (fseeko(ctx->f, (off_t)ctx->current, SEEK_SET) < 0) {
+                    zip_error_set(&ctx->error, ZIP_ER_SEEK, errno);
+                    return -1;
+                }
+            }
 
-	return (zip_int64_t)i;
+            if ((i=fread(buf, 1, (size_t)n, ctx->f)) == 0) {
+                if (ferror(ctx->f)) {
+                    zip_error_set(&ctx->error, ZIP_ER_READ, errno);
+                    return -1;
+                }
+            }
+            ctx->current += i;
+
+            return (zip_int64_t)i;
+            
+        case ZIP_SOURCE_REMOVE:
+            if (remove(ctx->fname) < 0) {
+                zip_error_set(&ctx->error, ZIP_ER_REMOVE, errno);
+                return -1;
+            }
+            return 0;
+            
+        case ZIP_SOURCE_ROLLBACK_WRITE:
+            if (ctx->fout) {
+                fclose(ctx->fout);
+                ctx->fout = NULL;
+            }
+            remove(ctx->tmpname);
+            ctx->tmpname = NULL;
+            return 0;
 	
-    case ZIP_SOURCE_CLOSE:
-	if (z->source_closed)
-	    return -1;
+        case ZIP_SOURCE_SEEK: {
+            zip_int64_t new_current;
+            int need_seek;
+	    zip_source_args_seek_t *args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
 
-	if (z->fname) {
-	    fclose(z->f);
-	    z->f = NULL;
-	}
-	return 0;
+	    if (args == NULL)
+		return -1;
+            
+            need_seek = ctx->closep;
+            
+            switch (args->whence) {
+                case SEEK_SET:
+                    new_current = args->offset;
+                    break;
+                    
+                case SEEK_END:
+                    if (ctx->end == 0) {
+                        if (fseeko(ctx->f, args->offset, SEEK_END) < 0) {
+                            zip_error_set(&ctx->error, ZIP_ER_SEEK, errno);
+                            return -1;
+                        }
+                        if ((new_current = ftello(ctx->f)) < 0) {
+                            zip_error_set(&ctx->error, ZIP_ER_SEEK, errno);
+                            return -1;
+                        }
+                        need_seek = 0;
+                    }
+                    else {
+                        new_current = (zip_int64_t)ctx->end + args->offset;
+                    }
+                    break;
+                case SEEK_CUR:
+                    new_current = (zip_int64_t)ctx->current + args->offset;
+                    break;
 
-    case ZIP_SOURCE_STAT:
-	if (z->source_closed)
-	    return -1;
+                default:
+                    zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
+                    return -1;
+            }
 
-        {
-	    if (len < sizeof(z->st))
+            if (new_current < 0 || (zip_uint64_t)new_current < ctx->start || (ctx->end != 0 && (zip_uint64_t)new_current > ctx->end)) {
+                zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
+                return -1;
+            }
+            
+            ctx->current = (zip_uint64_t)new_current;
+
+            if (need_seek) {
+                if (fseeko(ctx->f, (off_t)ctx->current, SEEK_SET) < 0) {
+                    zip_error_set(&ctx->error, ZIP_ER_SEEK, errno);
+                    return -1;
+                }
+            }
+            return 0;
+        }
+            
+        case ZIP_SOURCE_SEEK_WRITE: {
+            zip_int64_t offset, whence;
+            
+            if (len < sizeof(zip_int64_t)*2) {
+                zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
+                return -1;
+            }
+            offset = ((zip_int64_t *)data)[0];
+            whence = ((zip_int64_t *)data)[1];
+
+            if (fseeko(ctx->fout, offset, (int)whence) < 0) {
+                zip_error_set(&ctx->error, ZIP_ER_SEEK, errno);
+                return -1;
+            }
+            return 0;
+        }
+
+        case ZIP_SOURCE_STAT: {
+	    if (len < sizeof(ctx->st))
 		return -1;
 
-	    if (z->st.valid != 0)
-		memcpy(data, &z->st, sizeof(z->st));
+	    if (ctx->st.valid != 0)
+		memcpy(data, &ctx->st, sizeof(ctx->st));
 	    else {
 		struct zip_stat *st;
 		struct stat fst;
 		int err;
 	    
-		if (z->f)
-		    err = fstat(fileno(z->f), &fst);
+		if (ctx->f)
+		    err = fstat(fileno(ctx->f), &fst);
 		else
-		    err = stat(z->fname, &fst);
+		    err = stat(ctx->fname, &fst);
 
 		if (err != 0) {
-		    z->e[0] = ZIP_ER_READ; /* best match */
-		    z->e[1] = errno;
+                    zip_error_set(&ctx->error, ZIP_ER_READ, errno);
 		    return -1;
 		}
 
@@ -255,8 +406,8 @@
 		zip_stat_init(st);
 		st->mtime = fst.st_mtime;
 		st->valid |= ZIP_STAT_MTIME;
-		if (z->len != -1) {
-		    st->size = (zip_uint64_t)z->len;
+		if (ctx->end != 0) {
+                    st->size = ctx->end - ctx->start;
 		    st->valid |= ZIP_STAT_SIZE;
 		}
 		else if ((fst.st_mode&S_IFMT) == S_IFREG) {
@@ -264,67 +415,42 @@
 		    st->valid |= ZIP_STAT_SIZE;
 		}
 	    }
-	    return sizeof(z->st);
+	    return sizeof(ctx->st);
 	}
 
-    case ZIP_SOURCE_ERROR:
-	if (len < sizeof(int)*2)
-	    return -1;
+        case ZIP_SOURCE_SUPPORTS:
+	    return ctx->supports;
+            
+        case ZIP_SOURCE_TELL:
+            return (zip_int64_t)ctx->current;
+            
+        case ZIP_SOURCE_TELL_WRITE:
+        {
+            off_t ret = ftello(ctx->fout);
+            
+            if (ret < 0) {
+                zip_error_set(&ctx->error, ZIP_ER_TELL, errno);
+                return -1;
+            }
+            return ret;
+        }
+            
+        case ZIP_SOURCE_WRITE:
+        {
+            size_t ret;
+            
+	    clearerr(ctx->fout);
+            ret = fwrite(data, 1, len, ctx->fout);
+            if (ret != len || ferror(ctx->fout)) {
+                zip_error_set(&ctx->error, ZIP_ER_WRITE, errno);
+                return -1;
+            }
+            
+            return (zip_int64_t)ret;
+        }
 
-	memcpy(data, z->e, sizeof(int)*2);
-	return sizeof(int)*2;
-
-    case ZIP_SOURCE_FREE:
-	if (z->source_archive && !z->source_closed) {
-	    _zip_deregister_source(z->source_archive, state);
-	}
-	free(z->fname);
-	if (z->closep && z->f)
-	    fclose(z->f);
-	free(z);
-	return 0;
-
-    default:
-	;
+        default:
+            zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
+            return -1;
     }
-
-    return -1;
-}
-
-
-static void
-_zip_deregister_source(struct zip *za, void *ud)
-{
-    unsigned int i;
-
-    for (i=0; i<za->nsource; i++) {
-	if (za->source[i]->ud == ud) {
-	    za->source[i] = za->source[za->nsource-1];
-	    za->nsource--;
-	    break;
-	}
-    }
-}
-
-
-static int
-_zip_register_source(struct zip *za, struct zip_source *src)
-{
-    struct zip_source **source;
-
-    if (za->nsource+1 >= za->nsource_alloc) {
-	unsigned int n;
-	n = za->nsource_alloc + 10;
-	source = (struct zip_source **)realloc(za->source, n*sizeof(struct zip_source *));
-	if (source == NULL) {
-	    _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
-	    return -1;
-	}
-	za->nsource_alloc = n;
-	za->source = source;
-    }
-
-    za->source[za->nsource++] = src;
-
-    return 0;
 }
diff --git a/lib/zip_source_free.c b/lib/zip_source_free.c
index d1e8f3d..715efb5 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-2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -38,20 +38,40 @@
 
 
 ZIP_EXTERN void
-zip_source_free(struct zip_source *src)
+zip_source_free(zip_source_t *src)
+{
+    _zip_source_free_or_pop(src, 1);
+}
+
+
+void
+_zip_source_free_or_pop(zip_source_t *src, int recurse)
 {
     if (src == NULL)
 	return;
 
-    if (src->is_open)
-	zip_source_close(src);
-
-    if (src->src == NULL)
-	(void)src->cb.f(src->ud, NULL, 0, ZIP_SOURCE_FREE);
-    else {
-	(void)src->cb.l(src->src, src->ud, NULL, 0, ZIP_SOURCE_FREE);
-	zip_source_free(src->src);
+    if (src->refcount > 0) {
+        src->refcount--;
     }
-
+    if (src->refcount > 0) {
+        return;
+    }
+    
+    if (ZIP_SOURCE_IS_OPEN_READING(src)) {
+	zip_source_close(src);
+    }
+    if (ZIP_SOURCE_IS_OPEN_WRITING(src)) {
+        zip_source_rollback_write(src);
+    }
+    
+    if (src->source_archive && !src->source_closed) {
+        _zip_deregister_source(src->source_archive, src);
+    }
+    
+    (void)_zip_source_call(src, NULL, 0, ZIP_SOURCE_FREE);
+    
+    if (recurse && src->src) {
+        zip_source_free(src->src);
+    }
     free(src);
 }
diff --git a/lib/zip_source_function.c b/lib/zip_source_function.c
index 03f766e..884d5ad 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-2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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,20 @@
 ZIP_EXTERN struct zip_source *
 zip_source_function(struct zip *za, zip_source_callback zcb, void *ud)
 {
+    if (za == NULL) {
+        return NULL;
+    }
+    
+    return zip_source_function_create(zcb, ud, &za->error);
+}
+
+
+ZIP_EXTERN zip_source_t *
+zip_source_function_create(zip_source_callback zcb, void *ud, zip_error_t *error)
+{
     struct zip_source *zs;
 
-    if (za == NULL)
-	return NULL;
-
-    if ((zs=_zip_source_new(za)) == NULL)
+    if ((zs=_zip_source_new(error)) == NULL)
 	return NULL;
 
     zs->cb.f = zcb;
@@ -55,21 +63,32 @@
 }
 
 
+ZIP_EXTERN void
+zip_source_keep(zip_source_t *src)
+{
+    src->refcount++;
+}
+
+
 struct zip_source *
-_zip_source_new(struct zip *za)
+_zip_source_new(zip_error_t *error)
 {
     struct zip_source *src;
 
     if ((src=(struct zip_source *)malloc(sizeof(*src))) == NULL) {
-	_zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+        zip_error_set(error, ZIP_ER_MEMORY, 0);
 	return NULL;
     }
 
     src->src = NULL;
     src->cb.f = NULL;
     src->ud = NULL;
-    src->error_source = ZIP_LES_NONE;
-    src->is_open = 0;
+    src->open_count = 0;
+    src->write_state = ZIP_SOURCE_WRITE_CLOSED;
+    src->source_closed = 0;
+    src->source_archive = NULL;
+    src->refcount = 1;
+    zip_error_init(&src->error);
 
     return src;
 }
diff --git a/lib/zip_source_is_deleted.c b/lib/zip_source_is_deleted.c
new file mode 100644
index 0000000..e50cdd9
--- /dev/null
+++ b/lib/zip_source_is_deleted.c
@@ -0,0 +1,42 @@
+/*
+  zip_source_is_deleted.c -- was archive was removed?
+  Copyright (C) 2014 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>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include "zipint.h"
+
+
+ZIP_EXTERN int
+zip_source_is_deleted(zip_source_t *src)
+{
+    return src->write_state == ZIP_SOURCE_WRITE_REMOVED;
+}
diff --git a/lib/zip_source_layered.c b/lib/zip_source_layered.c
index 30450d0..3cf1f71 100644
--- a/lib/zip_source_layered.c
+++ b/lib/zip_source_layered.c
@@ -1,6 +1,6 @@
 /*
   zip_source_layered.c -- create layered source
-  Copyright (C) 2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 2009-2014 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>
@@ -38,17 +38,24 @@
 
 
 struct zip_source *
-zip_source_layered(struct zip *za, struct zip_source *src,
-		   zip_source_layered_callback cb, void *ud)
+zip_source_layered(struct zip *za, struct zip_source *src, zip_source_layered_callback cb, void *ud)
+{
+    if (za == NULL)
+        return NULL;
+
+    return zip_source_layered_create(src, cb, ud, &za->error);
+}
+
+
+zip_source_t *
+zip_source_layered_create(struct zip_source *src, zip_source_layered_callback cb, void *ud, zip_error_t *error)
 {
     struct zip_source *zs;
-
-    if (za == NULL)
-	return NULL;
-
-    if ((zs=_zip_source_new(za)) == NULL)
-	return NULL;
-
+    
+    if ((zs=_zip_source_new(error)) == NULL)
+        return NULL;
+    
+    zip_source_keep(src);
     zs->src = src;
     zs->cb.l = cb;
     zs->ud = ud;
diff --git a/lib/zip_source_open.c b/lib/zip_source_open.c
index 82efadb..46bf491 100644
--- a/lib/zip_source_open.c
+++ b/lib/zip_source_open.c
@@ -1,6 +1,6 @@
 /*
   zip_source_open.c -- open zip_source (prepare for reading)
-  Copyright (C) 2009-2013 Dieter Baron and Thomas Klausner
+  Copyright (C) 2009-2014 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>
@@ -38,37 +38,38 @@
 int
 zip_source_open(struct zip_source *src)
 {
-    zip_int64_t ret;
-
-    if (src->is_open) {
-	src->error_source = ZIP_LES_INVAL;
+    if (src->source_closed) {
+        return -1;
+    }
+    if (src->write_state == ZIP_SOURCE_WRITE_REMOVED) {
+        zip_error_set(&src->error, ZIP_ER_DELETED, 0);
 	return -1;
     }
-
-    if (src->src == NULL) {
-	if (src->cb.f(src->ud, NULL, 0, ZIP_SOURCE_OPEN) < 0)
-	    return -1;
+    if (ZIP_SOURCE_IS_OPEN_READING(src)) {
+        zip_error_set(&src->error, ZIP_ER_INVAL, 0);
+	return -1;
     }
-    else {
-	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) {
-	    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;
-	}
+ 
+    if (ZIP_SOURCE_IS_LAYERED(src)) {
+        if (ZIP_SOURCE_IS_OPEN_READING(src->src)) {
+            src->src->open_count++;
+        }
+        else {
+            if (zip_source_open(src->src) < 0) {
+                zip_error_set_from_source(&src->error, src->src);
+                return -1;
+            }
+        }
     }
 
-    src->is_open = 1;
+    if (_zip_source_call(src, NULL, 0, ZIP_SOURCE_OPEN) < 0) {
+        if (ZIP_SOURCE_IS_LAYERED(src)) {
+            zip_source_close(src->src);
+        }
+        return -1;
+    }
+    
+    src->open_count++;
     
     return 0;
 }
diff --git a/lib/zip_source_pkware.c b/lib/zip_source_pkware.c
index 78504b0..5e550e3 100644
--- a/lib/zip_source_pkware.c
+++ b/lib/zip_source_pkware.c
@@ -1,6 +1,6 @@
 /*
   zip_source_pkware.c -- Traditional PKWARE de/encryption routines
-  Copyright (C) 2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 2009-2014 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>
@@ -38,8 +38,7 @@
 #include "zipint.h"
 
 struct trad_pkware {
-    int e[2];
-
+    zip_error_t error;
     zip_uint32_t key[3];
 };
 
@@ -65,20 +64,20 @@
     struct zip_source *s2;
 
     if (password == NULL || src == NULL || em != ZIP_EM_TRAD_PKWARE) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return NULL;
     }
     if (flags & ZIP_CODEC_ENCODE) {
-	_zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0);
+	zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0);
 	return NULL;
     }
 
     if ((ctx=(struct trad_pkware *)malloc(sizeof(*ctx))) == NULL) {
-	_zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+	zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
 	return NULL;
     }
 
-    ctx->e[0] = ctx->e[1] = 0;
+    zip_error_init(&ctx->error);
 
     ctx->key[0] = KEY0;
     ctx->key[1] = KEY1;
@@ -134,13 +133,12 @@
     unsigned short dostime, dosdate;
 
     if ((n=zip_source_read(src, header, HEADERLEN)) < 0) {
-	zip_source_error(src, ctx->e, ctx->e+1);
+        zip_error_set_from_source(&ctx->error, src);
 	return -1;
     }
     
     if (n != HEADERLEN) {
-	ctx->e[0] = ZIP_ER_EOF;
-	ctx->e[1] = 0;
+        zip_error_set(&ctx->error, ZIP_ER_EOF, 0);
 	return -1;
     }
 
@@ -153,10 +151,8 @@
 
     _zip_u2d_time(st.mtime, &dostime, &dosdate);
 
-    if (header[HEADERLEN-1] != st.crc>>24
-	&& header[HEADERLEN-1] != dostime>>8) {
-	ctx->e[0] = ZIP_ER_WRONGPASSWD;
-	ctx->e[1] = 0;
+    if (header[HEADERLEN-1] != st.crc>>24 && header[HEADERLEN-1] != dostime>>8) {
+        zip_error_set(&ctx->error, ZIP_ER_WRONGPASSWD, 0);
 	return -1;
     }
 
@@ -174,24 +170,25 @@
     ctx = (struct trad_pkware *)ud;
 
     switch (cmd) {
-    case ZIP_SOURCE_OPEN:
-	if (decrypt_header(src, ctx) < 0)
-	    return -1;
-	return 0;
+        case ZIP_SOURCE_OPEN:
+            if (decrypt_header(src, ctx) < 0)
+                return -1;
+            return 0;
 
-    case ZIP_SOURCE_READ:
-	if ((n=zip_source_read(src, data, len)) < 0)
-	    return ZIP_SOURCE_ERR_LOWER;
+        case ZIP_SOURCE_READ:
+            if ((n=zip_source_read(src, data, len)) < 0) {
+                zip_error_set_from_source(&ctx->error, src);
+                return -1;
+            }
 
-	decrypt((struct trad_pkware *)ud, (zip_uint8_t *)data, (zip_uint8_t *)data, (zip_uint64_t)n,
-		0);
-	return n;
+            decrypt((struct trad_pkware *)ud, (zip_uint8_t *)data, (zip_uint8_t *)data, (zip_uint64_t)n, 0);
+            return n;
 
-    case ZIP_SOURCE_CLOSE:
-	return 0;
+        case ZIP_SOURCE_CLOSE:
+            return 0;
 
-    case ZIP_SOURCE_STAT:
-	{
+        case ZIP_SOURCE_STAT:
+        {
 	    struct zip_stat *st;
 
 	    st = (struct zip_stat *)data;
@@ -201,21 +198,23 @@
 	    /* TODO: deduce HEADERLEN from size for uncompressed */
 	    if (st->valid & ZIP_STAT_COMP_SIZE)
 		st->comp_size -= HEADERLEN;
-	}
-	return 0;
+	
+            return 0;
+        }
+            
+        case ZIP_SOURCE_SUPPORTS:
+            return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, -1);
 
-    case ZIP_SOURCE_ERROR:
-	memcpy(data, ctx->e, sizeof(int)*2);
-	return sizeof(int)*2;
+        case ZIP_SOURCE_ERROR:
+            return zip_error_to_data(&ctx->error, data, len);
 
-    case ZIP_SOURCE_FREE:
-	pkware_free(ctx);
-	return 0;
+        case ZIP_SOURCE_FREE:
+            pkware_free(ctx);
+            return 0;
 
-    default:
-	ctx->e[0] = ZIP_ER_INVAL;
-	ctx->e[1] = 0;
-	return -1;
+        default:
+            zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
+            return -1;
     }
 }
 
diff --git a/lib/zip_source_pop.c b/lib/zip_source_pop.c
index 49e3ae8..78c7ce1 100644
--- a/lib/zip_source_pop.c
+++ b/lib/zip_source_pop.c
@@ -1,6 +1,6 @@
 /*
   zip_source_pop.c -- pop top layer from zip data source
-  Copyright (C) 2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 2009-2014 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>
@@ -47,15 +47,7 @@
 
     lower = src->src;
 
-    if (lower == NULL)
-	zip_source_free(src);
-    else {
-	if (src->is_open)
-	    (void)src->cb.l(src, src->ud, NULL, 0, ZIP_SOURCE_CLOSE);
-	(void)src->cb.l(src, src->ud, NULL, 0, ZIP_SOURCE_FREE);
-	
-	free(src);
-    }
+    _zip_source_free_or_pop(src, 0);
 
     return lower;
 }
diff --git a/lib/zip_source_read.c b/lib/zip_source_read.c
index 910649d..8249b82 100644
--- a/lib/zip_source_read.c
+++ b/lib/zip_source_read.c
@@ -1,6 +1,6 @@
 /*
   zip_source_read.c -- read data from zip_source
-  Copyright (C) 2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 2009-2014 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>
@@ -38,25 +38,13 @@
 zip_int64_t
 zip_source_read(struct zip_source *src, void *data, zip_uint64_t len)
 {
-    zip_int64_t ret;
-
-    if (!src->is_open || len > ZIP_INT64_MAX || (len > 0 && data == NULL)) {
-	src->error_source = ZIP_LES_INVAL;
+    if (src->source_closed) {
+        return -1;
+    }
+    if (!ZIP_SOURCE_IS_OPEN_READING(src) || len > ZIP_INT64_MAX || (len > 0 && data == NULL)) {
+        zip_error_set(&src->error, ZIP_ER_INVAL, 0);
 	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;
+    return _zip_source_call(src, data, len, ZIP_SOURCE_READ);
 }
diff --git a/lib/zip_source_remove.c b/lib/zip_source_remove.c
new file mode 100644
index 0000000..470a5ed
--- /dev/null
+++ b/lib/zip_source_remove.c
@@ -0,0 +1,61 @@
+/*
+ zip_source_remove.c -- remove empty archive
+ Copyright (C) 2014 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>
+ 
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ 3. The names of the authors may not be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+ 
+ THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "zipint.h"
+
+
+int
+zip_source_remove(zip_source_t *src)
+{
+    if (src->write_state == ZIP_SOURCE_WRITE_REMOVED) {
+        return 0;
+    }
+    
+    if (ZIP_SOURCE_IS_OPEN_READING(src)) {
+	if (zip_source_close(src) < 0) {
+	    return -1;
+	}
+    }
+    if (src->write_state != ZIP_SOURCE_WRITE_CLOSED) {
+        zip_source_rollback_write(src);
+    }
+    
+    if (_zip_source_call(src, NULL, 0, ZIP_SOURCE_REMOVE) < 0) {
+        return -1;
+    }
+    
+    src->write_state = ZIP_SOURCE_WRITE_REMOVED;
+    
+    return 0;
+}
diff --git a/lib/zip_source_rollback_write.c b/lib/zip_source_rollback_write.c
new file mode 100644
index 0000000..a93cd20
--- /dev/null
+++ b/lib/zip_source_rollback_write.c
@@ -0,0 +1,47 @@
+/*
+  zip_source_rollback_write.c -- discard changes
+  Copyright (C) 2014 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>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include "zipint.h"
+
+
+void
+zip_source_rollback_write(zip_source_t *src)
+{
+    if (src->write_state != ZIP_SOURCE_WRITE_OPEN && src->write_state != ZIP_SOURCE_WRITE_FAILED) {
+	return;
+    }
+    
+    _zip_source_call(src, NULL, 0, ZIP_SOURCE_ROLLBACK_WRITE);
+    src->write_state = ZIP_SOURCE_WRITE_CLOSED;
+}
diff --git a/lib/zip_source_seek.c b/lib/zip_source_seek.c
new file mode 100644
index 0000000..25886f7
--- /dev/null
+++ b/lib/zip_source_seek.c
@@ -0,0 +1,55 @@
+/*
+  zip_source_seek.c -- seek to offset
+  Copyright (C) 2014 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>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include "zipint.h"
+
+
+int
+zip_source_seek(struct zip_source *src, zip_int64_t offset, int whence)
+{
+    zip_source_args_seek_t args;
+    
+    if (src->source_closed) {
+        return -1;
+    }
+    if (!ZIP_SOURCE_IS_OPEN_READING(src) || (whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END)) {
+        zip_error_set(&src->error, ZIP_ER_INVAL, 0);
+        return -1;
+    }
+
+    args.offset = offset;
+    args.whence = whence;
+    
+    return (_zip_source_call(src, &args, sizeof(args), ZIP_SOURCE_SEEK) < 0 ? -1 : 0);
+}
diff --git a/lib/zip_source_seek_write.c b/lib/zip_source_seek_write.c
new file mode 100644
index 0000000..6f4e351
--- /dev/null
+++ b/lib/zip_source_seek_write.c
@@ -0,0 +1,52 @@
+/*
+  zip_source_seek_write.c -- seek to offset for writing
+  Copyright (C) 2014 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>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include "zipint.h"
+
+
+int
+zip_source_seek_write(struct zip_source *src, zip_int64_t offset, int whence)
+{
+    zip_source_args_seek_t args;
+        
+    if (!ZIP_SOURCE_IS_OPEN_WRITING(src) || (whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END)) {
+        zip_error_set(&src->error, ZIP_ER_INVAL, 0);
+        return -1;
+    }
+    
+    args.offset = offset;
+    args.whence = whence;
+    
+    return (_zip_source_call(src, &args, sizeof(args), ZIP_SOURCE_SEEK_WRITE) < 0 ? -1 : 0);
+}
diff --git a/lib/zip_source_stat.c b/lib/zip_source_stat.c
index 3d5fc88..f70e8e9 100644
--- a/lib/zip_source_stat.c
+++ b/lib/zip_source_stat.c
@@ -1,6 +1,6 @@
 /*
   zip_source_stat.c -- get meta information from zip_source
-  Copyright (C) 2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 2009-2014 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>
@@ -38,33 +38,30 @@
 int
 zip_source_stat(struct zip_source *src, struct zip_stat *st)
 {
-    zip_int64_t ret;
-
+    if (src->source_closed) {
+        return -1;
+    }
     if (st == NULL) {
-	src->error_source = ZIP_LES_INVAL;
+        zip_error_set(&src->error, ZIP_ER_INVAL, 0);
 	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;
-    }
+    zip_stat_init(st);
     
+    if (ZIP_SOURCE_IS_LAYERED(src)) {
+        if (zip_source_stat(src->src, st) < 0) {
+            zip_error_set_from_source(&src->error, src->src);
+            return -1;
+        }
+    }
+
+    if (_zip_source_call(src, st, sizeof(*st), ZIP_SOURCE_STAT) < 0) {
+	return -1;
+    }
+
+    if ((st->valid & ZIP_STAT_COMP_METHOD) && ZIP_CM_IS_DEFAULT(st->comp_method)) {
+	st->valid &= ~ZIP_STAT_COMP_METHOD;
+    }
+
     return 0;
 }
diff --git a/lib/zip_source_supports.c b/lib/zip_source_supports.c
new file mode 100644
index 0000000..6f8d26c
--- /dev/null
+++ b/lib/zip_source_supports.c
@@ -0,0 +1,76 @@
+/*
+  zip_source_supports.c -- check for supported functions
+  Copyright (C) 2014 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>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <stdarg.h>
+
+#include "zipint.h"
+
+
+zip_int64_t
+zip_source_supports(struct zip_source *src)
+{
+    zip_int64_t ret;
+    
+    ret = _zip_source_call(src, NULL , 0, ZIP_SOURCE_SUPPORTS);
+    
+    if (ret < 0) {
+        ret = zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, -1);
+    }
+    
+    return ret;
+}
+
+
+zip_int64_t
+zip_source_make_command_bitmap(enum zip_source_cmd cmd0, ...)
+{
+    zip_int64_t bitmap;
+    va_list ap;
+    
+    bitmap = 1<<cmd0;
+    
+    
+    
+    va_start(ap, cmd0);
+    for (;;) {
+        int cmd = va_arg(ap, int);
+        if (cmd < 0) {
+            break;
+        }
+        bitmap |= 1<<cmd;
+    }
+    va_end(ap);
+    
+    return bitmap;
+}
diff --git a/lib/zip_source_tell.c b/lib/zip_source_tell.c
new file mode 100644
index 0000000..fdf2e77
--- /dev/null
+++ b/lib/zip_source_tell.c
@@ -0,0 +1,50 @@
+/*
+  zip_source_tell.c -- report current offset
+  Copyright (C) 2014 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>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include "zipint.h"
+
+
+zip_int64_t
+zip_source_tell(struct zip_source *src)
+{
+    if (src->source_closed) {
+        return -1;
+    }
+    if (!ZIP_SOURCE_IS_OPEN_READING(src)) {
+        zip_error_set(&src->error, ZIP_ER_INVAL, 0);
+        return -1;
+    }
+    
+    return _zip_source_call(src, NULL, 0, ZIP_SOURCE_TELL);
+}
diff --git a/lib/zip_source_tell_write.c b/lib/zip_source_tell_write.c
new file mode 100644
index 0000000..558fa0d
--- /dev/null
+++ b/lib/zip_source_tell_write.c
@@ -0,0 +1,47 @@
+/*
+  zip_source_tell_write.c -- report current offset for writing
+  Copyright (C) 2014 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>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include "zipint.h"
+
+
+zip_int64_t
+zip_source_tell_write(struct zip_source *src)
+{
+    if (!ZIP_SOURCE_IS_OPEN_WRITING(src)) {
+        zip_error_set(&src->error, ZIP_ER_INVAL, 0);
+        return -1;
+    }
+    
+    return _zip_source_call(src, NULL, 0, ZIP_SOURCE_TELL_WRITE);
+}
diff --git a/lib/zip_source_window.c b/lib/zip_source_window.c
index 9bc0f75..56dc256 100644
--- a/lib/zip_source_window.c
+++ b/lib/zip_source_window.c
@@ -1,6 +1,6 @@
 /*
   zip_source_window.c -- return part of lower source
-  Copyright (C) 2012-2013 Dieter Baron and Thomas Klausner
+  Copyright (C) 2012-2014 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>
@@ -38,42 +38,80 @@
 #include "zipint.h"
 
 struct window {
-    zip_uint64_t skip;
-    zip_uint64_t len;
-    zip_uint64_t left;
-    int e[2];
+    zip_uint64_t start;
+    zip_uint64_t end;
+    zip_uint64_t offset;
+    zip_stat_t stat;
+    zip_error_t error;
+    zip_int64_t supports;
+    int needs_seek;
 };
 
-static zip_int64_t window_read(struct zip_source *, void *, void *,
-			       zip_uint64_t, enum zip_source_cmd);
+static zip_int64_t window_read(struct zip_source *, void *, void *, zip_uint64_t, enum zip_source_cmd);
 
 
 struct zip_source *
 zip_source_window(struct zip *za, struct zip_source *src, zip_uint64_t start, zip_uint64_t len)
 {
+    return _zip_source_window_new(src, start, len, NULL, &za->error);
+}
+
+
+zip_source_t *
+_zip_source_window_new(zip_source_t *src, zip_uint64_t start, zip_uint64_t length, zip_stat_t *st, zip_error_t *error)
+{
     struct window *ctx;
-
-    if (src == NULL) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
-	return NULL;
+    
+    if (src == NULL || start + length < start) {
+        zip_error_set(error, ZIP_ER_INVAL, 0);
+        return NULL;
     }
-
+    
     if ((ctx=(struct window *)malloc(sizeof(*ctx))) == NULL) {
-	_zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
-	return NULL;
+        zip_error_set(error, ZIP_ER_MEMORY, 0);
+        return NULL;
     }
+    
+    ctx->start = start;
+    ctx->end = start + length;
+    zip_stat_init(&ctx->stat);
+    zip_error_init(&ctx->error);
+    ctx->supports = (zip_source_supports(src) & zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_SEEK, ZIP_SOURCE_TELL, -1)) | (zip_source_make_command_bitmap(ZIP_SOURCE_TELL, -1));
+    ctx->needs_seek = ctx->supports & zip_source_make_command_bitmap(ZIP_SOURCE_SEEK, -1);
+    
+    if (st) {
+        if (_zip_stat_merge(&ctx->stat, st, error) < 0) {
+            free(ctx);
+            return NULL;
+        }
+    }
+    
+    return zip_source_layered_create(src, window_read, ctx, error);
+}
 
-    ctx->skip = start;
-    ctx->len = len;
-    ctx->left = len;
 
-    return zip_source_layered(za, src, window_read, ctx);
+int
+_zip_source_set_source_archive(struct zip_source *src, struct zip *za)
+{
+    src->source_archive = za;
+    return _zip_register_source(za, src);
+}
+
+
+/* called by zip_discard to avoid operating on file from closed archive */
+void
+_zip_source_invalidate(struct zip_source *src)
+{
+    src->source_closed = 1;
+
+    if (zip_error_code_zip(&src->error) == ZIP_ER_OK) {
+        zip_error_set(&src->error, ZIP_ER_ZIPCLOSED, 0);
+    }
 }
 
 
 static zip_int64_t
-window_read(struct zip_source *src, void *_ctx, void *data,
-	    zip_uint64_t len, enum zip_source_cmd cmd)
+window_read(struct zip_source *src, void *_ctx, void *data, zip_uint64_t len, enum zip_source_cmd cmd)
 {
     struct window *ctx;
     zip_int64_t ret;
@@ -83,65 +121,152 @@
     ctx = (struct window *)_ctx;
 
     switch (cmd) {
-    case ZIP_SOURCE_OPEN:
-	for (n=0; n<ctx->skip; n+=(zip_uint64_t)ret) {
-	    i = (ctx->skip-n > sizeof(b) ? sizeof(b) : ctx->skip-n);
-	    if ((ret=zip_source_read(src, b, i)) < 0)
-		return ZIP_SOURCE_ERR_LOWER;
-	    if (ret==0) {
-		ctx->e[0] = ZIP_ER_EOF;
-		ctx->e[1] = 0;
+        case ZIP_SOURCE_CLOSE:
+            return 0;
+            
+        case ZIP_SOURCE_ERROR:
+            return zip_error_to_data(&ctx->error, data, len);
+            
+        case ZIP_SOURCE_FREE:
+            free(ctx);
+            return 0;
+
+        case ZIP_SOURCE_OPEN:
+            if (!ctx->needs_seek) {
+                for (n=0; n<ctx->start; n+=(zip_uint64_t)ret) {
+                    i = (ctx->start-n > sizeof(b) ? sizeof(b) : ctx->start-n);
+                    if ((ret=zip_source_read(src, b, i)) < 0) {
+                        zip_error_set_from_source(&ctx->error, src);
+                        return -1;
+                    }
+                    if (ret==0) {
+                        zip_error_set(&ctx->error, ZIP_ER_EOF, 0);
+                        return -1;
+                    }
+                }
+		
+            }
+	    ctx->offset = ctx->start;
+            return 0;
+
+        case ZIP_SOURCE_READ:
+            if (len > ctx->end - ctx->offset)
+                len = ctx->end - ctx->offset;
+            
+            if (len == 0)
+                return 0;
+            
+            if (ctx->needs_seek) {
+                if (zip_source_seek(src, (zip_int64_t)ctx->offset, SEEK_SET) < 0) {
+                    zip_error_set_from_source(&ctx->error, src);
+                    return -1;
+                }
+            }
+            
+            if ((ret=zip_source_read(src, data, len)) < 0) {
+                zip_error_set(&ctx->error, ZIP_ER_EOF, 0);
+                return -1;
+            }
+
+            ctx->offset += (zip_uint64_t)ret;
+
+            if (ret == 0) {
+                if (ctx->offset < ctx->end) {
+                    zip_error_set(&ctx->error, ZIP_ER_EOF, 0);
+                    return -1;
+                }
+            }
+            return ret;
+            
+        case ZIP_SOURCE_SEEK:
+        {
+            zip_int64_t new_offset;
+	    zip_source_args_seek_t *args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
+
+	    if (args == NULL)
 		return -1;
-	    }
-	}
-	return 0;
+            
+            switch (args->whence) {
+                case SEEK_CUR:
+                    new_offset = (zip_int64_t)ctx->offset + args->offset;
+                    break;
+                    
+                case SEEK_END:
+                    new_offset = (zip_int64_t)ctx->end + args->offset;
+                    break;
+                    
+                case SEEK_SET:
+                    new_offset = (zip_int64_t)ctx->start + args->offset;
+                    break;
+            }
+            
+            if (new_offset < (zip_int64_t)ctx->start || (zip_uint64_t)new_offset > ctx->end) {
+                zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
+                return -1;
+            }
+            
+            ctx->offset = (zip_uint64_t)new_offset;
+            return 0;
+        }
 
-    case ZIP_SOURCE_READ:
-	if (len > ctx->left)
-	    len = ctx->left;
-	
-	if (len == 0)
-	    return 0;
-
-	if ((ret=zip_source_read(src, data, len)) < 0)
-	    return ZIP_SOURCE_ERR_LOWER;
-
-	ctx->left -= (zip_uint64_t)ret;
-
-        if (ret == 0) {
-	    if (ctx->left > 0) {
-		ctx->e[0] = ZIP_ER_EOF;
-		ctx->e[1] = 0;
-		return -1;
-	    }
-	}
-	return ret;
-
-    case ZIP_SOURCE_CLOSE:
-	return 0;
-
-    case ZIP_SOURCE_STAT:
-	{
-	    struct zip_stat *st;
-
+       case ZIP_SOURCE_STAT:
+        {
+            struct zip_stat *st;
+            
 	    st = (struct zip_stat *)data;
+            
+            if (_zip_stat_merge(st, &ctx->stat, &ctx->error) < 0) {
+                return -1;
+            }
+            return 0;
+        }
+            
+        case ZIP_SOURCE_SUPPORTS:
+            return ctx->supports;
+            
+        case ZIP_SOURCE_TELL:
+            return (zip_int64_t)(ctx->offset - ctx->start);
+            
+       default:
+            zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
+            return -1;
+    }
+}
 
-	    st->size = ctx->len;
-	    st->valid |= ZIP_STAT_SIZE;
-	    st->valid &= ~(ZIP_STAT_CRC|ZIP_STAT_COMP_SIZE);
-	}
-	return 0;
-	
-    case ZIP_SOURCE_ERROR:
-	memcpy(data, ctx->e, sizeof(ctx->e));
-	return 0;
 
-    case ZIP_SOURCE_FREE:
-	free(ctx);
-	return 0;
+void
+_zip_deregister_source(struct zip *za, zip_source_t *src)
+{
+    unsigned int i;
+    
+    for (i=0; i<za->nopen_source; i++) {
+        if (za->open_source[i] == src) {
+            za->open_source[i] = za->open_source[za->nopen_source-1];
+            za->nopen_source--;
+            break;
+        }
+    }
+}
 
-    default:
-	return -1;
+
+int
+_zip_register_source(zip_t *za, zip_source_t *src)
+{
+    zip_source_t **open_source;
+    
+    if (za->nopen_source+1 >= za->nopen_source_alloc) {
+        unsigned int n;
+        n = za->nopen_source_alloc + 10;
+        open_source = (struct zip_source **)realloc(za->open_source, n*sizeof(struct zip_source *));
+        if (open_source == NULL) {
+            zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+            return -1;
+        }
+        za->nopen_source_alloc = n;
+        za->open_source = open_source;
     }
     
+    za->open_source[za->nopen_source++] = src;
+    
+    return 0;
 }
diff --git a/lib/zip_source_write.c b/lib/zip_source_write.c
new file mode 100644
index 0000000..bbf5d0b
--- /dev/null
+++ b/lib/zip_source_write.c
@@ -0,0 +1,47 @@
+/*
+  zip_source_write.c -- start a new file for writing
+  Copyright (C) 2014 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>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include "zipint.h"
+
+
+zip_int64_t
+zip_source_write(struct zip_source *src, const void *data, zip_uint64_t length)
+{
+    if (!ZIP_SOURCE_IS_OPEN_WRITING(src) || length > ZIP_INT64_MAX) {
+        zip_error_set(&src->error, ZIP_ER_INVAL, 0);
+        return -1;
+    }
+    
+    return _zip_source_call(src, (void *)data, length, ZIP_SOURCE_WRITE);
+}
diff --git a/lib/zip_source_zip.c b/lib/zip_source_zip.c
index 7ebfb81..8f85200 100644
--- a/lib/zip_source_zip.c
+++ b/lib/zip_source_zip.c
@@ -1,6 +1,6 @@
 /*
   zip_source_zip.c -- create data source from zip file
-  Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -43,7 +43,7 @@
 	       zip_flags_t flags, zip_uint64_t start, zip_int64_t len)
 {
     if (len < -1) {
-        _zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+        zip_error_set(&za->error, ZIP_ER_INVAL, 0);
         return NULL;
     }
         
diff --git a/lib/zip_source_zip_new.c b/lib/zip_source_zip_new.c
index 3f4fbd2..f8af9ad 100644
--- a/lib/zip_source_zip_new.c
+++ b/lib/zip_source_zip_new.c
@@ -51,18 +51,18 @@
 	return NULL;
 
     if (srcza == NULL ||  srcidx >= srcza->nentry) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return NULL;
     }
 
     if ((flags & ZIP_FL_UNCHANGED) == 0
 	&& (ZIP_ENTRY_DATA_CHANGED(srcza->entry+srcidx) || srcza->entry[srcidx].deleted)) {
-	_zip_error_set(&za->error, ZIP_ER_CHANGED, 0);
+	zip_error_set(&za->error, ZIP_ER_CHANGED, 0);
 	return NULL;
     }
 
     if (zip_stat_index(srcza, srcidx, flags|ZIP_FL_UNCHANGED, &st) < 0) {
-	_zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
 	return NULL;
     }
 
@@ -70,24 +70,24 @@
 	flags |= ZIP_FL_COMPRESSED;
 
     if ((start > 0 || len > 0) && (flags & ZIP_FL_COMPRESSED)) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return NULL;
     }
 
     /* overflow or past end of file */
     if ((start > 0 || len > 0) && (start+len < start || start+len > st.size)) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return NULL;
     }
 
     enc_impl = NULL;
     if (((flags & ZIP_FL_ENCRYPTED) == 0) && (st.encryption_method != ZIP_EM_NONE)) {
 	if (password == NULL) {
-	    _zip_error_set(&za->error, ZIP_ER_NOPASSWD, 0);
+	    zip_error_set(&za->error, ZIP_ER_NOPASSWD, 0);
 	    return NULL;
 	}
 	if ((enc_impl=_zip_get_encryption_implementation(st.encryption_method)) == NULL) {
-	    _zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0);
+	    zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0);
 	    return NULL;
 	}
     }
@@ -96,7 +96,7 @@
     if ((flags & ZIP_FL_COMPRESSED) == 0) {
 	if (st.comp_method != ZIP_CM_STORE) {
 	    if ((comp_impl=_zip_get_compression_implementation(st.comp_method)) == NULL) {
-		_zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
+		zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
 		return NULL;
 	    }
 	}
@@ -119,17 +119,17 @@
 	    st2.mtime = st.mtime;
 	    st2.valid = ZIP_STAT_SIZE|ZIP_STAT_COMP_SIZE|ZIP_STAT_COMP_METHOD|ZIP_STAT_MTIME;
 
-            /* TODO: check for overflow of st2.size */
-	    if ((src=_zip_source_file_or_p(za, NULL, srcza->zp, offset+start, (zip_int64_t)st2.size, 0, &st2)) == NULL)
-		return NULL;
+            if ((src = _zip_source_window_new(za->src, offset+start, st2.size, &st2, &za->error)) == NULL) {
+                return NULL;
+            }
 	}
 	else {
-            /* TODO: check for overflow of st.comp_size */
-	    if ((src=_zip_source_file_or_p(za, NULL, srcza->zp, offset, (zip_int64_t)st.comp_size, 0, &st)) == NULL)
-		return NULL;
+        if ((src = _zip_source_window_new(srcza->src, offset, st.comp_size, &st, &za->error)) == NULL) {
+            return NULL;
+        }
 	}
 	
-	if (_zip_source_filep_set_source_archive(src, srcza) < 0) {
+	if (_zip_source_set_source_archive(src, srcza) < 0) {
 	    zip_source_free(src);
 	    return NULL;
 	}
@@ -137,7 +137,6 @@
 	if (enc_impl) {
 	    if ((s2=enc_impl(za, src, st.encryption_method, 0, password)) == NULL) {
 		zip_source_free(src);
-		/* TODO: set error (how?) */
 		return NULL;
 	    }
 	    src = s2;
@@ -145,7 +144,6 @@
 	if (comp_impl) {
 	    if ((s2=comp_impl(za, src, st.comp_method, 0)) == NULL) {
 		zip_source_free(src);
-		/* TODO: set error (how?) */
 		return NULL;
 	    }
 	    src = s2;
@@ -155,7 +153,6 @@
 	    /* when reading the whole file, check for crc errors */
 	    if ((s2=zip_source_crc(za, src, 1)) == NULL) {
 		zip_source_free(src);
-		/* TODO: set error (how?) */
 		return NULL;
 	    }
 	    src = s2;
@@ -164,7 +161,6 @@
 	if (start+len > 0 && (comp_impl || enc_impl)) {
 	    if ((s2=zip_source_window(za, src, start, len ? len : st.size-start)) == NULL) {
 		zip_source_free(src);
-		/* TODO: set error (how?) (why?) */
 		return NULL;
 	    }
 	    src = s2;
diff --git a/lib/zip_stat.c b/lib/zip_stat.c
index 9f52639..b87182b 100644
--- a/lib/zip_stat.c
+++ b/lib/zip_stat.c
@@ -1,6 +1,6 @@
 /*
   zip_stat.c -- get information about file by name
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
diff --git a/lib/zip_stat_index.c b/lib/zip_stat_index.c
index 03eaad3..a58345b 100644
--- a/lib/zip_stat_index.c
+++ b/lib/zip_stat_index.c
@@ -1,6 +1,6 @@
 /*
   zip_stat_index.c -- get information about file by index
-  Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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,7 @@
     if ((flags & ZIP_FL_UNCHANGED) == 0
 	&& ZIP_ENTRY_DATA_CHANGED(za->entry+index)) {
 	if (zip_source_stat(za->entry[index].source, st) < 0) {
-	    _zip_error_set(&za->error, ZIP_ER_CHANGED, 0);
+	    zip_error_set(&za->error, ZIP_ER_CHANGED, 0);
 	    return -1;
 	}
     }
diff --git a/lib/zip_stat_init.c b/lib/zip_stat_init.c
index 204477b..0a8bf5a 100644
--- a/lib/zip_stat_init.c
+++ b/lib/zip_stat_init.c
@@ -1,6 +1,6 @@
 /*
   zip_stat_init.c -- initialize struct zip_stat.
-  Copyright (C) 2006-2009 Dieter Baron and Thomas Klausner
+  Copyright (C) 2006-2014 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>
@@ -31,6 +31,7 @@
   IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
+#include <string.h>
 
 #include "zipint.h"
 
@@ -48,3 +49,37 @@
     st->comp_method = ZIP_CM_STORE;
     st->encryption_method = ZIP_EM_NONE;
 }
+
+
+int
+_zip_stat_merge(zip_stat_t *dst, const zip_stat_t *src, zip_error_t *error)
+{
+    /* name is not merged, since zip_stat_t doesn't own it, and src may not be valid as long as dst */
+    if (src->valid & ZIP_STAT_INDEX) {
+        dst->index = src->index;
+    }
+    if (src->valid & ZIP_STAT_SIZE) {
+        dst->size = src->size;
+    }
+    if (src->valid & ZIP_STAT_COMP_SIZE) {
+        dst->comp_size = src->comp_size;
+    }
+    if (src->valid & ZIP_STAT_MTIME) {
+        dst->mtime = src->mtime;
+    }
+    if (src->valid & ZIP_STAT_CRC) {
+        dst->crc = src->crc;
+    }
+    if (src->valid & ZIP_STAT_COMP_METHOD) {
+        dst->comp_method = src->comp_method;
+    }
+    if (src->valid & ZIP_STAT_ENCRYPTION_METHOD) {
+        dst->encryption_method = src->encryption_method;
+    }
+    if (src->valid & ZIP_STAT_FLAGS) {
+        dst->flags = src->flags;
+    }
+    dst->valid |= src->valid;
+    
+    return 0;
+}
diff --git a/lib/zip_strerror.c b/lib/zip_strerror.c
index f08fdd2..548c021 100644
--- a/lib/zip_strerror.c
+++ b/lib/zip_strerror.c
@@ -1,6 +1,6 @@
 /*
   zip_sterror.c -- get string representation of zip error
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -38,5 +38,5 @@
 ZIP_EXTERN const char *
 zip_strerror(struct zip *za)
 {
-    return _zip_error_strerror(&za->error);
+    return zip_error_strerror(&za->error);
 }
diff --git a/lib/zip_string.c b/lib/zip_string.c
index 6c3f1c0..a731ebb 100644
--- a/lib/zip_string.c
+++ b/lib/zip_string.c
@@ -1,6 +1,6 @@
 /*
   zip_string.c -- string handling (with encoding)
-  Copyright (C) 2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 2012-2014 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>
@@ -145,12 +145,12 @@
 	expected_encoding = ZIP_ENCODING_CP437;
 	break;
     default:
-	_zip_error_set(error, ZIP_ER_INVAL, 0);
+	zip_error_set(error, ZIP_ER_INVAL, 0);
 	return NULL;
     }
 	
     if ((s=(struct zip_string *)malloc(sizeof(*s))) == NULL) {
-	_zip_error_set(error, ZIP_ER_MEMORY, 0);
+	zip_error_set(error, ZIP_ER_MEMORY, 0);
 	return NULL;
     }
 
@@ -169,7 +169,7 @@
     if (expected_encoding != ZIP_ENCODING_UNKNOWN) {
 	if (_zip_guess_encoding(s, expected_encoding) == ZIP_ENCODING_ERROR) {
 	    _zip_string_free(s);
-	    _zip_error_set(error, ZIP_ER_INVAL, 0);
+	    zip_error_set(error, ZIP_ER_INVAL, 0);
 	    return NULL;
 	}
     }
@@ -178,11 +178,11 @@
 }
 
 
-void
-_zip_string_write(const struct zip_string *s, FILE *f)
+int
+_zip_string_write(zip_t *za, const zip_string_t *s)
 {
     if (s == NULL)
-	return;
+	return 0;
     
-    fwrite(s->raw, s->length, 1, f);
+    return _zip_write(za, s->raw, s->length);
 }
diff --git a/lib/zip_unchange.c b/lib/zip_unchange.c
index 6e6d042..aa840d0 100644
--- a/lib/zip_unchange.c
+++ b/lib/zip_unchange.c
@@ -1,6 +1,6 @@
 /*
   zip_unchange.c -- undo changes to file in zip archive
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
@@ -50,14 +50,14 @@
     zip_int64_t i;
     
     if (idx >= za->nentry) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
     if (!allow_duplicates && za->entry[idx].changes && (za->entry[idx].changes->changed & ZIP_DIRENT_FILENAME)) {
 	i = _zip_name_locate(za, _zip_get_name(za, idx, ZIP_FL_UNCHANGED, NULL), 0, NULL);
 	if (i >= 0 && (zip_uint64_t)i != idx) {
-	    _zip_error_set(&za->error, ZIP_ER_EXISTS, 0);
+	    zip_error_set(&za->error, ZIP_ER_EXISTS, 0);
 	    return -1;
 	}
     }
diff --git a/lib/zip_unchange_all.c b/lib/zip_unchange_all.c
index 83b4166..c57cdf8 100644
--- a/lib/zip_unchange_all.c
+++ b/lib/zip_unchange_all.c
@@ -1,6 +1,6 @@
 /*
   zip_unchange.c -- undo changes to all files in zip archive
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
diff --git a/lib/zip_unchange_archive.c b/lib/zip_unchange_archive.c
index cf93375..291b826 100644
--- a/lib/zip_unchange_archive.c
+++ b/lib/zip_unchange_archive.c
@@ -1,6 +1,6 @@
 /*
   zip_unchange_archive.c -- undo global changes to ZIP archive
-  Copyright (C) 2006-2008 Dieter Baron and Thomas Klausner
+  Copyright (C) 2006-2014 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>
diff --git a/lib/zip_unchange_data.c b/lib/zip_unchange_data.c
index 2793a70..a95a81c 100644
--- a/lib/zip_unchange_data.c
+++ b/lib/zip_unchange_data.c
@@ -1,6 +1,6 @@
 /*
   zip_unchange_data.c -- undo helper function
-  Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2014 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>
diff --git a/lib/zip_utf-8.c b/lib/zip_utf-8.c
index 8eb5fe3..58c4195 100644
--- a/lib/zip_utf-8.c
+++ b/lib/zip_utf-8.c
@@ -1,6 +1,6 @@
 /*
   zip_utf-8.c -- UTF-8 support functions for libzip
-  Copyright (C) 2011-2012 Dieter Baron and Thomas Klausner
+  Copyright (C) 2011-2014 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>
@@ -233,7 +233,7 @@
 	buflen += _zip_unicode_to_utf8_len(_cp437_to_unicode[cp437buf[i]]);
 
     if ((utf8buf=(zip_uint8_t*)malloc(buflen)) == NULL) {
-	_zip_error_set(error, ZIP_ER_MEMORY, 0);
+	zip_error_set(error, ZIP_ER_MEMORY, 0);
 	return NULL;
     }
 
diff --git a/lib/zipint.h b/lib/zipint.h
index e4cb066..ace8a0b 100644
--- a/lib/zipint.h
+++ b/lib/zipint.h
@@ -165,14 +165,17 @@
 #define ZIP_EXT_ATTRIB_DEFAULT_DIR	(0040777u<<16)
 
 
+#define ZIP_MAX(a, b)		((a) > (b) ? (a) : (b))
+#define ZIP_MIN(a, b)		((a) < (b) ? (a) : (b))
+
 /* This section contains API that won't materialize like this.  It's
    placed in the internal section, pending cleanup. */
 
-typedef struct zip_source *(*zip_compression_implementation)(struct zip *,
-						     struct zip_source *,
+typedef zip_source_t *(*zip_compression_implementation)(struct zip *,
+						     zip_source_t *,
 						     zip_int32_t, int);
-typedef struct zip_source *(*zip_encryption_implementation)(struct zip *,
-						    struct zip_source *,
+typedef zip_source_t *(*zip_encryption_implementation)(struct zip *,
+						    zip_source_t *,
 						    zip_uint16_t, int,
 						    const char *);
 
@@ -189,38 +192,30 @@
    user-supplied compression/encryption implementation is finished.
    Thus we will keep it private for now. */
 
-typedef zip_int64_t (*zip_source_layered_callback)(struct zip_source *, void *,
-						   void *, zip_uint64_t,
-						   enum zip_source_cmd);
-
-void zip_source_close(struct zip_source *);
-struct zip_source *zip_source_crc(struct zip *, struct zip_source *,
-				  int);
-struct zip_source *zip_source_deflate(struct zip *,
-				      struct zip_source *,
-				      zip_int32_t, int);
-void zip_source_error(struct zip_source *, int *, int *);
-struct zip_source *zip_source_layered(struct zip *,
-				      struct zip_source *,
-				      zip_source_layered_callback,
-				      void *);
-int zip_source_open(struct zip_source *);
-struct zip_source *zip_source_pkware(struct zip *,
-				     struct zip_source *,
-				     zip_uint16_t, int,
-				     const char *);
-zip_int64_t zip_source_read(struct zip_source *, void *,
-			    zip_uint64_t);
-int zip_source_stat(struct zip_source *, struct zip_stat *);
-struct zip_source *zip_source_window(struct zip *, struct zip_source *,
-				     zip_uint64_t, zip_uint64_t);
+typedef zip_int64_t (*zip_source_layered_callback)(zip_source_t *, void *, void *, zip_uint64_t, enum zip_source_cmd);
+int zip_source_begin_write(zip_source_t *);
+int zip_source_commit_write(zip_source_t *);
+zip_source_t *zip_source_crc(struct zip *, zip_source_t *, int);
+zip_source_t *zip_source_deflate(struct zip *, zip_source_t *, zip_int32_t, int);
+zip_source_t *zip_source_layered(struct zip *, zip_source_t *, zip_source_layered_callback, void *);
+zip_source_t *zip_source_layered_create(zip_source_t *src, zip_source_layered_callback cb, void *ud, zip_error_t *error);
+zip_source_t *zip_source_pkware(struct zip *, zip_source_t *, zip_uint16_t, int, const char *);
+int zip_source_remove(zip_source_t *);
+void zip_source_rollback_write(zip_source_t *);
+int zip_source_seek(zip_source_t *, zip_int64_t, int);
+int zip_source_seek_write(zip_source_t *, zip_int64_t, int);
+zip_int64_t zip_source_supports(zip_source_t *src);
+zip_int64_t zip_source_tell(zip_source_t *);
+zip_int64_t zip_source_tell_write(zip_source_t *);
+zip_source_t *zip_source_window(struct zip *, zip_source_t *, zip_uint64_t, zip_uint64_t);
+zip_int64_t zip_source_write(zip_source_t *, const void *, zip_uint64_t);
 
 
 /* This function will probably remain private.  It is not needed to
    implement compression/encryption routines.  (We should probably
    rename it to _zip_source_pop.) */
 
-struct zip_source *zip_source_pop(struct zip_source *);
+zip_source_t *zip_source_pop(zip_source_t *);
 
 
 /* error source for layered sources */
@@ -255,38 +250,38 @@
     ZIP_ENCODING_ERROR          /* should be UTF-8 but isn't */
 };
 
-/* error information */
+typedef enum zip_encoding_type zip_encoding_type_t;
 
-struct zip_error {
-    int zip_err;	/* libzip error code (ZIP_ER_*) */
-    int sys_err;	/* copy of errno (E*) or zlib error code */
-    char *str;		/* string representation or NULL */
-};
+typedef struct zip_cdir zip_cdir_t;
+typedef struct zip_dirent zip_dirent_t;
+typedef struct zip_entry zip_entry_t;
+typedef struct zip_extra_field zip_extra_field_t;
+typedef struct zip_string zip_string_t;
+
 
 /* zip archive, part of API */
 
 struct zip {
-    char *zn;				/* file name */
-    FILE *zp;				/* file */
+    zip_source_t *src;                  /* data source for archive */
     unsigned int open_flags;		/* flags passed to zip_open */
-    struct zip_error error;		/* error information */
+    zip_error_t error;                  /* error information */
 
     unsigned int flags;			/* archive global flags */
     unsigned int ch_flags;		/* changed archive global flags */
 
     char *default_password;		/* password used when no other supplied */
 
-    struct zip_string *comment_orig;	/* archive comment */
-    struct zip_string *comment_changes; /* changed archive comment */
+    zip_string_t *comment_orig;         /* archive comment */
+    zip_string_t *comment_changes;  /* changed archive comment */
     int comment_changed;		/* whether archive comment was changed */
 
     zip_uint64_t nentry;		/* number of entries */
     zip_uint64_t nentry_alloc;		/* number of entries allocated */
-    struct zip_entry *entry;		/* entries */
+    zip_entry_t *entry;                 /* entries */
 
-    unsigned int nsource;		/* number of open sources using archive */
-    unsigned int nsource_alloc;		/* number of sources allocated */
-    struct zip_source **source;		/* open sources using archive */
+    unsigned int nopen_source;		/* number of open sources using archive */
+    unsigned int nopen_source_alloc;	/* number of sources allocated */
+    zip_source_t **open_source;         /* open sources using archive */
     
     char *tempdir;                      /* custom temp dir (needed e.g. for OS X sandboxing) */
 };
@@ -294,10 +289,10 @@
 /* file in zip archive, part of API */
 
 struct zip_file {
-    struct zip *za;		/* zip archive containing this file */
-    struct zip_error error;	/* error information */
+    zip_t *za;		/* zip archive containing this file */
+    zip_error_t error;	/* error information */
     int eof;
-    struct zip_source *src;	/* data source */
+    zip_source_t *src;	/* data source */
 };
 
 /* zip archive directory entry (central or local) */
@@ -312,64 +307,79 @@
 
 struct zip_dirent {
     zip_uint32_t changed;
-    int local_extra_fields_read;		/*      whether we already read in local header extra fields */
-    int cloned;                                 /*      whether this instance is cloned, and thus shares non-changed strings */
+    int local_extra_fields_read;	/*      whether we already read in local header extra fields */
+    int cloned;                         /*      whether this instance is cloned, and thus shares non-changed strings */
 
-    zip_uint16_t version_madeby;		/* (c)  version of creator */
-    zip_uint16_t version_needed;		/* (cl) version needed to extract */
-    zip_uint16_t bitflags;			/* (cl) general purpose bit flag */
-    zip_int32_t comp_method;			/* (cl) compression method used (uint16 and ZIP_CM_DEFAULT (-1)) */
-    time_t last_mod;				/* (cl) time of last modification */
-    zip_uint32_t crc;				/* (cl) CRC-32 of uncompressed data */
-    zip_uint64_t comp_size;			/* (cl) size of compressed data */
-    zip_uint64_t uncomp_size;			/* (cl) size of uncompressed data */
-    struct zip_string *filename;		/* (cl) file name (NUL-terminated) */
-    struct zip_extra_field *extra_fields;	/* (cl) extra fields, parsed */
-    struct zip_string *comment;			/* (c)  file comment */
-    zip_uint32_t disk_number;			/* (c)  disk number start */
-    zip_uint16_t int_attrib;			/* (c)  internal file attributes */
-    zip_uint32_t ext_attrib;			/* (c)  external file attributes */
-    zip_uint64_t offset;			/* (c)  offset of local header */
+    zip_uint16_t version_madeby;	/* (c)  version of creator */
+    zip_uint16_t version_needed;	/* (cl) version needed to extract */
+    zip_uint16_t bitflags;		/* (cl) general purpose bit flag */
+    zip_int32_t comp_method;		/* (cl) compression method used (uint16 and ZIP_CM_DEFAULT (-1)) */
+    time_t last_mod;			/* (cl) time of last modification */
+    zip_uint32_t crc;			/* (cl) CRC-32 of uncompressed data */
+    zip_uint64_t comp_size;		/* (cl) size of compressed data */
+    zip_uint64_t uncomp_size;		/* (cl) size of uncompressed data */
+    zip_string_t *filename;		/* (cl) file name (NUL-terminated) */
+    zip_extra_field_t *extra_fields;	/* (cl) extra fields, parsed */
+    zip_string_t *comment;		/* (c)  file comment */
+    zip_uint32_t disk_number;		/* (c)  disk number start */
+    zip_uint16_t int_attrib;		/* (c)  internal file attributes */
+    zip_uint32_t ext_attrib;		/* (c)  external file attributes */
+    zip_uint64_t offset;		/* (c)  offset of local header */
 };
 
 /* zip archive central directory */
 
 struct zip_cdir {
-    struct zip_entry *entry;	 		/* directory entries */
-    zip_uint64_t nentry;			/* number of entries */
-    zip_uint64_t nentry_alloc;			/* number of entries allocated */
+    zip_entry_t *entry;	 		/* directory entries */
+    zip_uint64_t nentry;		/* number of entries */
+    zip_uint64_t nentry_alloc;		/* number of entries allocated */
 
-    off_t size;                                 /* size of central directory */
-    off_t offset;		 		/* offset of central directory in file */
-    struct zip_string *comment;			/* zip archive comment */
+    zip_uint64_t size;                  /* size of central directory */
+    zip_uint64_t offset;		/* offset of central directory in file */
+    zip_string_t *comment;		/* zip archive comment */
 };
 
 struct zip_extra_field {
-    struct zip_extra_field *next;
-    zip_flags_t flags;				/* in local/central header */
-    zip_uint16_t id;				/* header id */
-    zip_uint16_t size;				/* data size */
+    zip_extra_field_t *next;
+    zip_flags_t flags;			/* in local/central header */
+    zip_uint16_t id;			/* header id */
+    zip_uint16_t size;			/* data size */
     zip_uint8_t *data;
 };
 
+enum zip_source_write_state {
+    ZIP_SOURCE_WRITE_CLOSED,    /* write is not in progress */
+    ZIP_SOURCE_WRITE_OPEN,      /* write is in progress */
+    ZIP_SOURCE_WRITE_FAILED,    /* commit failed, only rollback allowed */
+    ZIP_SOURCE_WRITE_REMOVED    /* file was removed */
+};
+typedef enum zip_source_write_state zip_source_write_state_t;
 
 struct zip_source {
-    struct zip_source *src;
+    zip_source_t *src;
     union {
 	zip_source_callback f;
 	zip_source_layered_callback l;
     } cb;
     void *ud;
-    enum zip_les error_source;
-    int is_open;
+    zip_error_t error;
+    unsigned int open_count;    /* number of times source was opened (directly or as lower layer) */
+    zip_source_write_state_t write_state;          /* whether source is open for writing */
+    int source_closed;          /* set if source archive is closed */
+    zip_t *source_archive;      /* zip archive we're reading from, NULL if not from archive */
+    unsigned int refcount;
 };
 
+#define ZIP_SOURCE_IS_OPEN_READING(src) ((src)->open_count > 0)
+#define ZIP_SOURCE_IS_OPEN_WRITING(src) ((src)->write_state == ZIP_SOURCE_WRITE_OPEN)
+#define ZIP_SOURCE_IS_LAYERED(src)  ((src)->src != NULL)
+
 /* entry in zip archive directory */
 
 struct zip_entry {
-    struct zip_dirent *orig;
-    struct zip_dirent *changes;
-    struct zip_source *source;
+    zip_dirent_t *orig;
+    zip_dirent_t *changes;
+    zip_source_t *source;
     int deleted;
 };
 
@@ -392,6 +402,8 @@
     const char *name;
 };
 
+typedef struct zip_filelist zip_filelist_t;
+
 
 extern const char * const _zip_err_str[];
 extern const int _zip_nerr_str;
@@ -411,7 +423,8 @@
 void _zip_cdir_free(struct zip_cdir *);
 int _zip_cdir_grow(struct zip_cdir *, zip_uint64_t, struct zip_error *);
 struct zip_cdir *_zip_cdir_new(zip_uint64_t, struct zip_error *);
-zip_int64_t _zip_cdir_write(struct zip *, const struct zip_filelist *, zip_uint64_t, FILE *);
+zip_int64_t _zip_cdir_write(struct zip *za, const struct zip_filelist *filelist, zip_uint64_t survivors);
+void _zip_deregister_source(zip_t *za, zip_source_t *src);
 
 struct zip_dirent *_zip_dirent_clone(const struct zip_dirent *);
 void _zip_dirent_free(struct zip_dirent *);
@@ -419,11 +432,12 @@
 void _zip_dirent_init(struct zip_dirent *);
 int _zip_dirent_needs_zip64(const struct zip_dirent *, zip_flags_t);
 struct zip_dirent *_zip_dirent_new(void);
-int _zip_dirent_read(struct zip_dirent *, FILE *, const unsigned char **,
-		     zip_uint64_t *, int, struct zip_error *);
-zip_int32_t _zip_dirent_size(FILE *, zip_uint16_t, struct zip_error *);
+int _zip_dirent_read(struct zip_dirent *zde, zip_source_t *src, 
+		 const unsigned char **bufp, zip_uint64_t *leftp, int local,
+		 zip_error_t *error);
+zip_int32_t _zip_dirent_size(zip_source_t *src, zip_uint16_t, struct zip_error *);
 void _zip_dirent_torrent_normalize(struct zip_dirent *);
-int _zip_dirent_write(struct zip_dirent *, FILE *, zip_flags_t, struct zip_error *);
+int _zip_dirent_write(struct zip *za, struct zip_dirent *dirent, zip_flags_t flags);
 
 struct zip_extra_field *_zip_ef_clone(const struct zip_extra_field *, struct zip_error *);
 struct zip_extra_field *_zip_ef_delete_by_id(struct zip_extra_field *, zip_uint16_t, zip_uint16_t, zip_flags_t);
@@ -434,19 +448,16 @@
 struct zip_extra_field *_zip_ef_parse(const zip_uint8_t *, zip_uint16_t, zip_flags_t, struct zip_error *);
 struct zip_extra_field *_zip_ef_remove_internal(struct zip_extra_field *);
 zip_uint16_t _zip_ef_size(const struct zip_extra_field *, zip_flags_t);
-void _zip_ef_write(const struct zip_extra_field *, zip_flags_t, FILE *);
+int _zip_ef_write(struct zip *za, const struct zip_extra_field *ef, zip_flags_t flags);
 
 void _zip_entry_finalize(struct zip_entry *);
 void _zip_entry_init(struct zip_entry *);
 
 void _zip_error_clear(struct zip_error *);
-void _zip_error_copy(struct zip_error *, const struct zip_error *);
-void _zip_error_fini(struct zip_error *);
 void _zip_error_get(const struct zip_error *, int *, int *);
-void _zip_error_init(struct zip_error *);
-void _zip_error_set(struct zip_error *, int, int);
-void _zip_error_set_from_source(struct zip_error *, struct zip_source *);
-const char *_zip_error_strerror(struct zip_error *);
+
+void _zip_error_copy(zip_error_t *dst, const struct zip_error *src);
+void _zip_error_set_from_source(zip_error_t *, zip_source_t *);
 
 const zip_uint8_t *_zip_extract_extra_field_by_id(struct zip_error *, zip_uint16_t, int, const zip_uint8_t *, zip_uint16_t, zip_uint16_t *);
 
@@ -454,30 +465,45 @@
 int _zip_file_fillbuf(void *, size_t, struct zip_file *);
 zip_uint64_t _zip_file_get_offset(const struct zip *, zip_uint64_t, struct zip_error *);
 
-int _zip_filerange_crc(FILE *, off_t, off_t, uLong *, struct zip_error *);
+int _zip_filerange_crc(zip_source_t *src, zip_uint64_t offset, zip_uint64_t length, uLong *crcp, struct zip_error *error);
 
 struct zip_dirent *_zip_get_dirent(struct zip *, zip_uint64_t, zip_flags_t, struct zip_error *);
 
 enum zip_encoding_type _zip_guess_encoding(struct zip_string *, enum zip_encoding_type);
 zip_uint8_t *_zip_cp437_to_utf8(const zip_uint8_t * const, zip_uint32_t, zip_uint32_t *, struct zip_error *);
 
-struct zip *_zip_open(const char *, FILE *, unsigned int, int *);
+struct zip *_zip_open(zip_source_t *, unsigned int, struct zip_error *);
 
+void _zip_put_16(zip_uint8_t **p, zip_uint16_t i);
+void _zip_put_32(zip_uint8_t **p, zip_uint32_t i);
+void _zip_put_64(zip_uint8_t **p, zip_uint64_t i);
+void _zip_put_data(zip_uint8_t **p, const char *s, size_t len);
+
+int _zip_read(zip_source_t *src, zip_uint8_t *data, zip_uint64_t length, struct zip_error *error);
+int _zip_read_at_offset(zip_source_t *src, zip_uint64_t offset, unsigned char *b, size_t length, struct zip_error *error);
 int _zip_read_local_ef(struct zip *, zip_uint64_t);
+struct zip_string *_zip_read_string(const zip_uint8_t **buf, zip_source_t *src, zip_uint16_t len, int nulp, struct zip_error *error);
+int _zip_register_source(zip_t *za, zip_source_t *src);
 
-struct zip_source *_zip_source_file_or_p(struct zip *, const char *, FILE *, zip_uint64_t, zip_int64_t, int, const struct zip_stat *);
-void _zip_source_filep_invalidate(struct zip_source *);
-int _zip_source_filep_set_source_archive(struct zip_source *, struct zip *);
-struct zip_source *_zip_source_new(struct zip *);
-struct zip_source *_zip_source_zip_new(struct zip *, struct zip *, zip_uint64_t, zip_flags_t, zip_uint64_t, zip_uint64_t, const char *);
+void _zip_set_open_error(int *zep, const struct zip_error *err, int ze);
 
+zip_int64_t _zip_source_call(zip_source_t *src, void *data, zip_uint64_t length, enum zip_source_cmd command);
+zip_source_t *_zip_source_file_or_p(const char *, FILE *, zip_uint64_t, zip_int64_t, int, const struct zip_stat *, zip_error_t *error);
+void _zip_source_invalidate(zip_source_t *src);
+void _zip_source_free_or_pop(zip_source_t *src, int recurse);
+zip_source_t *_zip_source_new(zip_error_t *error);
+int _zip_source_set_source_archive(zip_source_t *, struct zip *);
+zip_source_t *_zip_source_window_new(zip_source_t *src, zip_uint64_t start, zip_uint64_t length, zip_stat_t *st, zip_error_t *error);
+zip_source_t *_zip_source_zip_new(struct zip *, struct zip *, zip_uint64_t, zip_flags_t, zip_uint64_t, zip_uint64_t, const char *);
+
+int _zip_stat_merge(zip_stat_t *dst, const zip_stat_t *src, zip_error_t *error);
 int _zip_string_equal(const struct zip_string *, const struct zip_string *);
 void _zip_string_free(struct zip_string *);
 zip_uint32_t _zip_string_crc32(const struct zip_string *);
 const zip_uint8_t *_zip_string_get(struct zip_string *, zip_uint32_t *, zip_flags_t, struct zip_error *);
 zip_uint16_t _zip_string_length(const struct zip_string *);
 struct zip_string *_zip_string_new(const zip_uint8_t *, zip_uint16_t, zip_flags_t, struct zip_error *);
-void _zip_string_write(const struct zip_string *, FILE *);
+int _zip_string_write(struct zip *za, const struct zip_string *string);
 
 int _zip_changed(const struct zip *, zip_uint64_t *);
 const char *_zip_get_name(struct zip *, zip_uint64_t, zip_flags_t, struct zip_error *);
@@ -485,21 +511,15 @@
 void *_zip_memdup(const void *, size_t, struct zip_error *);
 zip_int64_t _zip_name_locate(struct zip *, const char *, zip_flags_t, struct zip_error *);
 struct zip *_zip_new(struct zip_error *);
-zip_uint16_t _zip_read2(const zip_uint8_t **);
-zip_uint32_t _zip_read4(const zip_uint8_t **);
-zip_uint64_t _zip_read8(const zip_uint8_t **);
-zip_uint8_t *_zip_read_data(const zip_uint8_t **, FILE *, size_t, int, struct zip_error *);
-zip_int64_t _zip_file_replace(struct zip *, zip_uint64_t, const char *, struct zip_source *, zip_flags_t);
+zip_uint16_t _zip_get_16(const zip_uint8_t **);
+zip_uint32_t _zip_get_32(const zip_uint8_t **);
+zip_uint64_t _zip_get_64(const zip_uint8_t **);
+zip_uint8_t *_zip_read_data(const zip_uint8_t **bufferp, zip_source_t *src, size_t length, int nulp, struct zip_error *error);
+zip_int64_t _zip_file_replace(struct zip *, zip_uint64_t, const char *, zip_source_t *, zip_flags_t);
 int _zip_set_name(struct zip *, zip_uint64_t, const char *, zip_flags_t);
 void _zip_u2d_time(time_t, zip_uint16_t *, zip_uint16_t *);
 int _zip_unchange(struct zip *, zip_uint64_t, int);
 void _zip_unchange_data(struct zip_entry *);
-
-void _zip_poke4(zip_uint32_t, zip_uint8_t **);
-void _zip_poke8(zip_uint64_t, zip_uint8_t **);
-void _zip_write2(zip_uint16_t, FILE *);
-void _zip_write4(zip_uint32_t, FILE *);
-void _zip_write8(zip_uint64_t, FILE *);
-
+int _zip_write(struct zip *za, const void *data, zip_uint64_t length);
 
 #endif /* zipint.h */