Add API for getting/setting extra field data.

Doesn't parse the fields yet, just provides them to the library user.

Open points:
* cdir extra field entries are not changed
* heuristics for finding cdir should be adapted

Based on patch provided by Jono Spiro <jono.spiro@gmail.com> (with
minimal changes).

--HG--
branch : HEAD
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 29ba802..4d80b1e 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -85,6 +85,7 @@
   zip_get_compression_implementation.c
   zip_get_encryption_implementation.c
   zip_get_file_comment.c
+  zip_get_file_extra.c
   zip_get_name.c
   zip_get_num_files.c
   zip_memdup.c
@@ -97,6 +98,7 @@
   zip_set_archive_flag.c
   zip_set_default_password.c
   zip_set_file_comment.c
+  zip_set_file_extra.c
   zip_set_name.c
   zip_source_buffer.c
   zip_source_close.c
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 6faf4a1..b12e83c 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -42,6 +42,7 @@
 	zip_get_compression_implementation.c \
 	zip_get_encryption_implementation.c \
 	zip_get_file_comment.c \
+	zip_get_file_extra.c \
 	zip_get_num_files.c \
 	zip_get_name.c \
 	zip_memdup.c \
@@ -54,6 +55,7 @@
 	zip_set_archive_flag.c \
 	zip_set_default_password.c \
 	zip_set_file_comment.c \
+	zip_set_file_extra.c \
 	zip_source_buffer.c \
 	zip_source_close.c \
 	zip_source_crc.c \
diff --git a/lib/zip.h b/lib/zip.h
index 94ee69b..261e550 100644
--- a/lib/zip.h
+++ b/lib/zip.h
@@ -230,6 +230,8 @@
 ZIP_EXTERN int zip_get_archive_flag(struct zip *, int, int);
 ZIP_EXTERN const char *zip_get_file_comment(struct zip *, zip_uint64_t,
 					    int *, int);
+ZIP_EXTERN const char *zip_get_file_extra(struct zip *, zip_uint64_t,
+					  int *, int);
 ZIP_EXTERN const char *zip_get_name(struct zip *, zip_uint64_t, int);
 ZIP_EXTERN int zip_get_num_files(struct zip *);
 ZIP_EXTERN int zip_name_locate(struct zip *, const char *, int);
@@ -241,6 +243,8 @@
 ZIP_EXTERN int zip_set_default_password(struct zip *, const char *);
 ZIP_EXTERN int zip_set_file_comment(struct zip *, zip_uint64_t,
 				    const char *, int);
+ZIP_EXTERN int zip_set_file_extra(struct zip *, zip_uint64_t,
+				  const char *, int);
 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 *,
diff --git a/lib/zip_close.c b/lib/zip_close.c
index ded5345..6b7f8d2 100644
--- a/lib/zip_close.c
+++ b/lib/zip_close.c
@@ -217,6 +217,22 @@
 	    cd->entry[j].filename_len = de.filename_len;
 	}
 
+	if (za->entry[i].ch_extra_len != -1) {
+	    free(de.extrafield);
+	    if ((de.extrafield=malloc(za->entry[i].ch_extra_len)) == NULL) {
+		error = 1;
+		break;
+	    }
+	    memcpy(de.extrafield, za->entry[i].ch_extra, za->entry[i].ch_extra_len);
+	    de.extrafield_len = za->entry[i].ch_extra_len;
+	    /* as the rest of cd entries, its malloc/free is done by za */
+	    /* TODO unsure if this should also be set in the CD --
+	     * not done for now
+	    cd->entry[j].extrafield = za->entry[i].ch_extra;
+	    cd->entry[j].extrafield_len = za->entry[i].ch_extra_len;
+	    */
+	}
+
 	if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 0
 	    && za->entry[i].ch_comment_len != -1) {
 	    /* as the rest of cd entries, its malloc/free is done by za */
@@ -557,6 +573,7 @@
 
     for (i=0; i<za->nentry; i++) {
 	if ((za->entry[i].state != ZIP_ST_UNCHANGED)
+	    || (za->entry[i].ch_extra_len != -1)
 	    || (za->entry[i].ch_comment_len != -1))
 	    changed = 1;
 	if (za->entry[i].state != ZIP_ST_DELETED)
diff --git a/lib/zip_entry_free.c b/lib/zip_entry_free.c
index c50c943..e8a7770 100644
--- a/lib/zip_entry_free.c
+++ b/lib/zip_entry_free.c
@@ -44,6 +44,9 @@
 {
     free(ze->ch_filename);
     ze->ch_filename = NULL;
+    free(ze->ch_extra);
+    ze->ch_extra = NULL;
+    ze->ch_extra_len = -1;
     free(ze->ch_comment);
     ze->ch_comment = NULL;
     ze->ch_comment_len = -1;
diff --git a/lib/zip_entry_new.c b/lib/zip_entry_new.c
index a22dd40..26d5c6a 100644
--- a/lib/zip_entry_new.c
+++ b/lib/zip_entry_new.c
@@ -67,6 +67,8 @@
     ze->state = ZIP_ST_UNCHANGED;
 
     ze->ch_filename = NULL;
+    ze->ch_extra = NULL;
+    ze->ch_extra_len = -1;
     ze->ch_comment = NULL;
     ze->ch_comment_len = -1;
     ze->source = NULL;
diff --git a/lib/zip_get_file_extra.c b/lib/zip_get_file_extra.c
new file mode 100644
index 0000000..afa507b
--- /dev/null
+++ b/lib/zip_get_file_extra.c
@@ -0,0 +1,58 @@
+/*
+  zip_get_file_extra.c -- get file extra field
+  Copyright (C) 2006-2010 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 const char *
+zip_get_file_extra(struct zip *za, zip_uint64_t idx, int *lenp, int flags)
+{
+    if (idx >= za->nentry) {
+	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	return NULL;
+    }
+
+    if ((flags & ZIP_FL_UNCHANGED)
+	|| (za->entry[idx].ch_extra_len == -1)) {
+	if (lenp != NULL)
+	    *lenp = za->cdir->entry[idx].extrafield_len;
+	return za->cdir->entry[idx].extrafield;
+    }
+
+    if (lenp != NULL)
+	*lenp = za->entry[idx].ch_extra_len;
+    return za->entry[idx].ch_extra;
+}
diff --git a/lib/zip_set_file_extra.c b/lib/zip_set_file_extra.c
new file mode 100644
index 0000000..9b80bde
--- /dev/null
+++ b/lib/zip_set_file_extra.c
@@ -0,0 +1,72 @@
+/*
+  zip_set_file_extra.c -- set extra field for file in archive
+  Copyright (C) 2006-2010 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 <stdlib.h>
+
+#include "zipint.h"
+
+
+
+ZIP_EXTERN int
+zip_set_file_extra(struct zip *za, zip_uint64_t idx,
+		   const char *extra, int len)
+{
+    char *tmpext;
+
+    if (idx >= za->nentry
+	|| len < 0 || len > MAXEXTLEN
+	|| (len > 0 && extra == NULL)) {
+	_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);
+	return -1;
+    }
+
+    if (len > 0) {
+	if ((tmpext=(char *)_zip_memdup(extra, len, &za->error)) == NULL)
+	    return -1;
+    }
+    else
+	tmpext = NULL;
+
+    free(za->entry[idx].ch_extra);
+    za->entry[idx].ch_extra = tmpext;
+    za->entry[idx].ch_extra_len = len;
+
+    return 0;
+}
diff --git a/lib/zip_unchange.c b/lib/zip_unchange.c
index 58ec54b..e64186e 100644
--- a/lib/zip_unchange.c
+++ b/lib/zip_unchange.c
@@ -72,6 +72,9 @@
 	za->entry[idx].ch_filename = NULL;
     }
 
+    free(za->entry[idx].ch_extra);
+    za->entry[idx].ch_extra = NULL;
+    za->entry[idx].ch_extra_len = -1;
     free(za->entry[idx].ch_comment);
     za->entry[idx].ch_comment = NULL;
     za->entry[idx].ch_comment_len = -1;
diff --git a/lib/zipint.h b/lib/zipint.h
index 57304d2..e105461 100644
--- a/lib/zipint.h
+++ b/lib/zipint.h
@@ -103,6 +103,7 @@
 #define CDENTRYSIZE         46u
 #define LENTRYSIZE          30
 #define MAXCOMLEN        65536
+#define MAXEXTLEN        65536
 #define EOCDLEN             22
 #define CDBUFSIZE       (MAXCOMLEN+EOCDLEN)
 #define BUFSIZE		8192
@@ -275,6 +276,8 @@
     enum zip_state state;
     struct zip_source *source;
     char *ch_filename;
+    char *ch_extra;
+    int ch_extra_len;
     char *ch_comment;
     int ch_comment_len;
 };