Clean up misc. todos.

- Implement missing extra field functions
- add flag for zip_file_add that overwrites any existing entry with same name
- set "version needed"/"version made by" correctly (zip64)
- zip_replace: set compression method to default unless user-set
- reset compression specific bits in bitflags when (re)compressing
- rename zip_get_{compression,encryption}_implementation to _zip_*
diff --git a/TODO b/TODO
index db730d8..9af4980 100644
--- a/TODO
+++ b/TODO
@@ -9,7 +9,6 @@
 
 API Issues
 ==========
-! clean up first version of extra field support
 * compression/crypt implementations: how to set error code on failure
 * compression/crypt error messages a la ZIP_ER_ZLIB (no detailed info passing)
 * check arguments for every entry point into libzip
@@ -17,7 +16,6 @@
 
 Features
 ========
-! Implement missing extra field functions
 ! Document extra field functions
 ! Document API changes
   . _set_name allows NULL (document)
@@ -26,9 +24,10 @@
   . document ZIP_DISABLE_DEPRECATED (libzip man page)
   . renames of zip_add, zip_rename, zip_replace, zip_add_dir, zip_{set,get}_file_comment
   . NULL is now allowed for file names (zip_add, zip_add_dir, zip_rename)
-! add flag for zip_file_add that overwrites any existing entry with same name
+! document ZIP_FL_OVERWRITE (flag for zip_file_add that overwrites any existing entry with same name)
 * do not compress if storing is smaller
 
+! test extra fields api
 * I/O methods
 * support streaming output (creating new archive to e.g. stdout)
 * add functions to:
@@ -55,10 +54,7 @@
 
 Bugs
 ====
-! set "version needed"/"version made by" correctly (zip64)
 ! check inconsistent file test case result
-! zip_replace: set compression method to default unless user-set
-! reset compression specific bits in bitflags when (re)compressing
 * split zip archive torrentzip state from user requested torrentzip state
 * check for limits imposed by format (central dir size, file size, extra fields, ...)
 * _zip_u2d_time: handle localtime(3) failure
@@ -72,7 +68,6 @@
 
 Cleanup
 =======
-! rename zip_get_{compression,encryption}_implementation to _zip_*
 * get rid of zip_get_{compression,encryption}_implementation
 * use zip_*int*_t internally
 * completely get rid of off_t
diff --git a/lib/zip.h b/lib/zip.h
index c04c9ed..cf3edb2 100644
--- a/lib/zip.h
+++ b/lib/zip.h
@@ -80,6 +80,7 @@
 /*                           1024    reserved for internal use */
 #define ZIP_FL_ENC_UTF_8     2048 /* string is UTF-8 encoded */
 #define ZIP_FL_ENC_CP437     4096 /* string is CP437 encoded */
+#define ZIP_FL_OVERWRITE     8192 /* zip_file_add: if file with name exists, overwrite (replace) it */
 
 /* archive global flags flags */
 
diff --git a/lib/zip_close.c b/lib/zip_close.c
index 1ac9153..098a9c2 100644
--- a/lib/zip_close.c
+++ b/lib/zip_close.c
@@ -282,7 +282,7 @@
 	st.comp_method = ZIP_CM_STORE;
     }
 
-    if (de->comp_method == ZIP_CM_DEFAULT && st.comp_method != ZIP_CM_STORE)
+    if (ZIP_CM_IS_DEFAULT(de->comp_method) && st.comp_method != ZIP_CM_STORE)
 	de->comp_method = st.comp_method;
     else if (de->comp_method == ZIP_CM_STORE && (st.valid & ZIP_STAT_SIZE)) {
 	st.valid |= ZIP_STAT_COMP_SIZE;
@@ -302,8 +302,8 @@
 	de->uncomp_size = st.size;
 	
 	if ((st.valid & ZIP_STAT_COMP_SIZE) == 0) {
-	    if ((((de->comp_method == ZIP_CM_DEFLATE || de->comp_method == ZIP_CM_DEFAULT) && st.size > MAX_DEFLATE_SIZE_32)
-		 || de->comp_method != ZIP_CM_STORE && de->comp_method != ZIP_CM_DEFLATE && de->comp_method != ZIP_CM_DEFAULT))
+	    if ((((de->comp_method == ZIP_CM_DEFLATE || ZIP_CM_IS_DEFAULT(de->comp_method)) && st.size > MAX_DEFLATE_SIZE_32)
+		 || de->comp_method != ZIP_CM_STORE && de->comp_method != ZIP_CM_DEFLATE && !ZIP_CM_IS_DEFAULT(de->comp_method)))
 		flags |= ZIP_FL_FORCE_ZIP64;
 	}
 	else
@@ -317,12 +317,12 @@
 	return -1;
 
 
-    if (st.comp_method == ZIP_CM_STORE || (de->comp_method != ZIP_CM_DEFAULT && st.comp_method != de->comp_method)) {
+    if (st.comp_method == ZIP_CM_STORE || (ZIP_CM_IS_DEFAULT(de->comp_method) && st.comp_method != de->comp_method)) {
 	struct zip_source *s_store, *s_crc;
 	zip_compression_implementation comp_impl;
 	
 	if (st.comp_method != ZIP_CM_STORE) {
-	    if ((comp_impl=zip_get_compression_implementation(st.comp_method)) == NULL) {
+	    if ((comp_impl=_zip_get_compression_implementation(st.comp_method)) == NULL) {
 		_zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
 		return -1;
 	    }
@@ -342,7 +342,7 @@
 
 	/* XXX: 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) {
+	    if ((comp_impl=_zip_get_compression_implementation(de->comp_method)) == NULL) {
 		_zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
 		zip_source_pop(s_crc);
 		if (s_store != src)
diff --git a/lib/zip_dirent.c b/lib/zip_dirent.c
index 77cf4a3..2457c96 100644
--- a/lib/zip_dirent.c
+++ b/lib/zip_dirent.c
@@ -249,7 +249,7 @@
     de->changed = 0;
     de->local_extra_fields_read = 0;
 
-    de->version_madeby = 0;
+    de->version_madeby = 20;
     de->version_needed = 20; /* 2.0 */
     de->bitflags = 0;
     de->comp_method = ZIP_CM_DEFAULT;
@@ -627,6 +627,7 @@
     struct zip_extra_field *ef;
     zip_uint8_t ef_zip64[24], *ef_zip64_p;
     int is_zip64;
+    int is_really_zip64;
 
     ef = NULL;
 
@@ -684,10 +685,15 @@
 	is_zip64 = 1;
     }
 
+    if ((flags & (ZIP_FL_LOCAL|ZIP_FL_FORCE_ZIP64)) == (ZIP_FL_LOCAL|ZIP_FL_FORCE_ZIP64))
+	is_really_zip64 = _zip_dirent_needs_zip64(de, flags);
+    else
+	is_really_zip64 = is_zip64;
+    
     if ((flags & ZIP_FL_LOCAL) == 0)
-	_zip_write2(de->version_madeby, fp);
-    _zip_write2(de->version_needed, fp);    /* XXX: at least 4.5 if zip64 */
-    _zip_write2(de->bitflags, fp);
+	_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(de->comp_method, fp);
 
     _zip_u2d_time(de->last_mod, &dostime, &dosdate);
diff --git a/lib/zip_extra_field_api.c b/lib/zip_extra_field_api.c
index 1c45dd9..bbb0f75 100644
--- a/lib/zip_extra_field_api.c
+++ b/lib/zip_extra_field_api.c
@@ -40,8 +40,13 @@
 ZIP_EXTERN int
 zip_file_extra_field_delete(struct zip *za, zip_uint64_t idx, zip_uint16_t ef_idx, zip_flags_t flags)
 {
-    /* XXX */
-    return -1;
+    struct zip_dirent *de;
+    
+    if ((de=_zip_get_dirent(za, idx, flags, &za->error)) == NULL)
+	return -1;
+
+    de->extra_fields = _zip_ef_delete_by_id(de->extra_fields, ZIP_EXTRA_FIELD_ALL, ef_idx, flags);
+    return 0;
 }
 
 
@@ -49,8 +54,13 @@
 ZIP_EXTERN int
 zip_file_extra_field_delete_by_id(struct zip *za, zip_uint64_t idx, zip_uint16_t ef_id, zip_uint16_t ef_idx, zip_flags_t flags)
 {
-    /* XXX */
-    return -1;
+    struct zip_dirent *de;
+    
+    if ((de=_zip_get_dirent(za, idx, flags, &za->error)) == NULL)
+	return -1;
+
+    de->extra_fields = _zip_ef_delete_by_id(de->extra_fields, ef_id, ef_idx, flags);
+    return 0;
 }
 
 
@@ -177,9 +187,73 @@
 ZIP_EXTERN int
 zip_file_extra_field_set(struct zip *za, zip_uint64_t idx, zip_uint16_t ef_id, zip_uint16_t ef_idx, const zip_uint8_t *data, zip_uint16_t len, zip_flags_t flags)
 {
-    /* XXX: ZIP_ER_INVAL if ef_id == ZIP_EF_ZIP64 */
-    /* XXX: size of ef + new <= ZIP_UINT16_MAX */
+    struct zip_dirent *de;
+    zip_uint16_t ls, cs;
+    struct zip_extra_field *ef, *ef_prev, *ef_new;
+    int i, found;
 
-    /* XXX */
-    return -1;
+    if ((de=_zip_get_dirent(za, idx, flags, &za->error)) == NULL)
+	return -1;
+
+    if (ef_id == ZIP_EF_ZIP64 || ef_id == ZIP_EF_UTF_8_NAME || ef_id == ZIP_EF_UTF_8_COMMENT) {
+	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	return -1;
+    }
+
+    if (flags & ZIP_EF_LOCAL)
+	ls = _zip_ef_size(de->extra_fields, ZIP_EF_LOCAL);
+    else
+	ls = 0;
+    if (flags & ZIP_EF_CENTRAL)
+	cs = _zip_ef_size(de->extra_fields, ZIP_EF_CENTRAL);
+    else
+	cs = 0;
+
+    if (((zip_uint32_t)(ls > cs ? ls : cs)) + len + 4 > ZIP_UINT16_MAX) {
+	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	return -1;
+    }
+
+    ef = de->extra_fields;
+    ef_prev = NULL;
+    found = 0;
+
+    for (; ef; ef=ef->next) {
+	if (ef->id == ef_id && (ef->flags & ZIP_EF_BOTH) == (flags & ZIP_EF_BOTH)) {
+	    if (i == ef_idx) {
+		found = 1;
+		break;
+	    }
+	    i++;
+	}
+	ef_prev = ef;
+    }
+
+    if (i < ef_idx && ef_idx != ZIP_EXTRA_FIELD_NEW) {
+	_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);
+	return -1;
+    }
+
+    if (found) {
+	ef_new->next = ef->next;
+	ef->next = NULL;
+	_zip_ef_free(ef);
+	if (ef_prev)
+	    ef_prev->next = ef_new;
+	else
+	    de->extra_fields = ef_new;
+    }
+    else if (ef) {
+	ef_new->next = ef->next;
+	ef->next = ef_new;
+    }
+    else
+	de->extra_fields = ef_new;
+    
+    return 0;
 }
diff --git a/lib/zip_file_replace.c b/lib/zip_file_replace.c
index 14da8ad..b23a646 100644
--- a/lib/zip_file_replace.c
+++ b/lib/zip_file_replace.c
@@ -67,11 +67,16 @@
 
     za_nentry_prev = za->nentry;
     if (idx == ZIP_UINT64_MAX) {
-	zip_int64_t i;
+	zip_int64_t i = -1;
 	
-	/* create and use new entry, used by zip_add */
-	if ((i=_zip_add_entry(za)) < 0)
-	    return -1;
+	if (flags & ZIP_FL_OVERWRITE)
+	    i = zip_name_locate(za, name, flags);
+
+	if (i == -1) {
+	    /* create and use new entry, used by zip_add */
+	    if ((i=_zip_add_entry(za)) < 0)
+		return -1;
+	}
 	idx = i;
     }
     
@@ -87,6 +92,14 @@
      * needed for a double add of the same file name */
     _zip_unchange_data(za->entry+idx);
 
+    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)
+	    za->entry[idx].changes = _zip_dirent_clone(za->entry[idx].orig);
+
+	za->entry[idx].changes->comp_method = ZIP_CM_REPLACED_DEFAULT;
+	za->entry[idx].changes->changed |= ZIP_DIRENT_COMP_METHOD;
+    }
+	
     za->entry[idx].source = source;
 
     return idx;
diff --git a/lib/zip_get_compression_implementation.c b/lib/zip_get_compression_implementation.c
index 82128f3..aa4a160 100644
--- a/lib/zip_get_compression_implementation.c
+++ b/lib/zip_get_compression_implementation.c
@@ -37,10 +37,10 @@
 
 
 
-ZIP_EXTERN zip_compression_implementation
-zip_get_compression_implementation(zip_int32_t cm)
+zip_compression_implementation
+_zip_get_compression_implementation(zip_int32_t cm)
 {
-    if (cm == ZIP_CM_DEFLATE || cm == ZIP_CM_DEFAULT)
+    if (cm == ZIP_CM_DEFLATE || ZIP_CM_IS_DEFAULT(cm))
 	return zip_source_deflate;
     return NULL;
 }
diff --git a/lib/zip_get_encryption_implementation.c b/lib/zip_get_encryption_implementation.c
index d83698c..7dcb992 100644
--- a/lib/zip_get_encryption_implementation.c
+++ b/lib/zip_get_encryption_implementation.c
@@ -37,8 +37,8 @@
 
 
 
-ZIP_EXTERN zip_encryption_implementation
-zip_get_encryption_implementation(zip_uint16_t em)
+zip_encryption_implementation
+_zip_get_encryption_implementation(zip_uint16_t em)
 {
     if (em == ZIP_EM_TRAD_PKWARE)
 	return zip_source_pkware;
diff --git a/lib/zip_set_file_compression.c b/lib/zip_set_file_compression.c
index 51a74a2..bbcaa65 100644
--- a/lib/zip_set_file_compression.c
+++ b/lib/zip_set_file_compression.c
@@ -60,20 +60,22 @@
 
     e = za->entry+idx;
     
-    if (method != (e->orig ? e->orig->comp_method : ZIP_CM_DEFAULT)) {
+    if (method == ZIP_CM_DEFAULT && e->orig == NULL) {
+	if (e->changes) {
+	    e->changes->changed &= ~ZIP_DIRENT_COMP_METHOD;
+	    if (e->changes->changed == 0) {
+		_zip_dirent_free(e->changes);
+		e->changes = NULL;
+	    }
+	}
+    }
+    else {
 	if (e->changes == NULL)
 	    e->changes = _zip_dirent_clone(e->orig);
 
 	e->changes->comp_method = method;
 	e->changes->changed |= ZIP_DIRENT_COMP_METHOD;
     }
-    else if (e->changes) {
-	e->changes->changed &= ~ZIP_DIRENT_COMP_METHOD;
-	if (e->changes->changed == 0) {
-	    _zip_dirent_free(e->changes);
-	    e->changes = NULL;
-	}
-    }
     
     return 0;
 }
diff --git a/lib/zip_source_deflate.c b/lib/zip_source_deflate.c
index e651400..5c26740 100644
--- a/lib/zip_source_deflate.c
+++ b/lib/zip_source_deflate.c
@@ -67,7 +67,7 @@
     struct deflate *ctx;
     struct zip_source *s2;
 
-    if (src == NULL || (cm != ZIP_CM_DEFLATE && cm != ZIP_CM_DEFAULT)) {
+    if (src == NULL || (cm != ZIP_CM_DEFLATE && !ZIP_CM_IS_DEFAULT(cm))) {
 	_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 d3aa339..44e41ab 100644
--- a/lib/zip_source_zip_new.c
+++ b/lib/zip_source_zip_new.c
@@ -91,7 +91,7 @@
 	    _zip_error_set(&za->error, ZIP_ER_NOPASSWD, 0);
 	    return NULL;
 	}
-	if ((enc_impl=zip_get_encryption_implementation(st.encryption_method)) == NULL) {
+	if ((enc_impl=_zip_get_encryption_implementation(st.encryption_method)) == NULL) {
 	    _zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0);
 	    return NULL;
 	}
@@ -100,7 +100,7 @@
     comp_impl = NULL;
     if ((flags & ZIP_FL_COMPRESSED) == 0) {
 	if (st.comp_method != ZIP_CM_STORE) {
-	    if ((comp_impl=zip_get_compression_implementation(st.comp_method)) == NULL) {
+	    if ((comp_impl=_zip_get_compression_implementation(st.comp_method)) == NULL) {
 		_zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
 		return NULL;
 	    }
diff --git a/lib/zip_unchange_data.c b/lib/zip_unchange_data.c
index 105ea3a..fa25feb 100644
--- a/lib/zip_unchange_data.c
+++ b/lib/zip_unchange_data.c
@@ -43,6 +43,14 @@
 	ze->source = NULL;
     }
 
+    if (ze->changes != NULL && (ze->changes->changed & ZIP_DIRENT_COMP_METHOD) && ze->changes->comp_method == ZIP_CM_REPLACED_DEFAULT) {
+	ze->changes->changed &= ~ZIP_DIRENT_COMP_METHOD;
+	if (ze->changes->changed == 0) {
+	    _zip_dirent_free(ze->changes);
+	    ze->changes = NULL;
+	}
+    }
+
     ze->deleted = 0;
 }
 
diff --git a/lib/zipint.h b/lib/zipint.h
index 44c5baf..d7737af 100644
--- a/lib/zipint.h
+++ b/lib/zipint.h
@@ -118,6 +118,10 @@
 #define CDBUFSIZE       (MAXCOMLEN+EOCDLEN+EOCD64LOCLEN)
 #define BUFSIZE		8192
 
+#define ZIP_CM_REPLACED_DEFAULT (-2)
+
+#define ZIP_CM_IS_DEFAULT(x)	((x) == ZIP_CM_DEFAULT || (x) == ZIP_CM_REPLACED_DEFAULT)
+
 #define ZIP_EF_UTF_8_COMMENT	0x6375
 #define ZIP_EF_UTF_8_NAME	0x7075
 #define ZIP_EF_ZIP64		0x0001
@@ -135,8 +139,8 @@
 						    zip_uint16_t, int,
 						    const char *);
 
-zip_compression_implementation zip_get_compression_implementation(zip_int32_t);
-zip_encryption_implementation zip_get_encryption_implementation(zip_uint16_t);
+zip_compression_implementation _zip_get_compression_implementation(zip_int32_t);
+zip_encryption_implementation _zip_get_encryption_implementation(zip_uint16_t);