Add details to error messages regarding zip archive inconsistencies.
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 07fdc7e..3d0aa70 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -195,11 +195,13 @@
 
 # create zip_err_str.h from zip.h
 file(READ ${PROJECT_SOURCE_DIR}/lib/zip.h zip_h)
-string(REGEX MATCHALL "#define ZIP_ER_([A-Z_]+) ([0-9]+)[ \t]+/([-*0-9a-zA-Z ']*)/" zip_h_err ${zip_h})
+string(REGEX MATCHALL "#define ZIP_ER_([A-Z0-9_]+) ([0-9]+)[ \t]+/([-*0-9a-zA-Z, ']*)/" zip_h_err ${zip_h})
+file(READ ${PROJECT_SOURCE_DIR}/lib/zipint.h zipint_h)
+string(REGEX MATCHALL "#define ZIP_ER_DETAIL_([A-Z0-9_]+) ([0-9]+)[ \t]+/([-*0-9a-zA-Z, ']*)/" zipint_h_err ${zipint_h})
 set(zip_err_str [=[
 /*
   This file was generated automatically by CMake
-  from zip.h\; make changes there.
+  from zip.h and zipint.h\; make changes there.
 */
 
 #include "zipint.h"
@@ -208,8 +210,8 @@
 ]=])
 set(zip_err_type)
 foreach(errln ${zip_h_err})
-  string(REGEX MATCH "#define ZIP_ER_([A-Z_]+) ([0-9]+)[ \t]+/([-*0-9a-zA-Z ']*)/" err_t_tt ${errln})
-  string(REGEX MATCH "([N|S|Z]+) ([-0-9a-zA-Z ']*)" err_t_tt "${CMAKE_MATCH_3}")
+  string(REGEX MATCH "#define ZIP_ER_([A-Z0-9_]+) ([0-9]+)[ \t]+/([-*0-9a-zA-Z, ']*)/" err_t_tt ${errln})
+  string(REGEX MATCH "([L|N|S|Z]+) ([-0-9a-zA-Z,, ']*)" err_t_tt "${CMAKE_MATCH_3}")
   string(APPEND zip_err_type "    ${CMAKE_MATCH_1},\n")
   string(STRIP "${CMAKE_MATCH_2}" err_t_tt)
   string(APPEND zip_err_str "    \"${err_t_tt}\",\n")
@@ -218,11 +220,29 @@
 
 const int _zip_nerr_str = sizeof(_zip_err_str)/sizeof(_zip_err_str[0])\;
 
+#define L ZIP_ET_LIBZIP
 #define N ZIP_ET_NONE
 #define S ZIP_ET_SYS
 #define Z ZIP_ET_ZLIB
 
+#define E true
+#define G false
+
 const int _zip_err_type[] = {
 ]=])
 string(APPEND zip_err_str "${zip_err_type}}\;\n")
+string(APPEND zip_err_str [=[
+
+const struct _zip_err_detail _zip_err_details[] = {
+]=])
+foreach(errln ${zipint_h_err})
+  string(REGEX MATCH "#define ZIP_ER_DETAIL_([A-Z0-9_]+) ([0-9]+)[ \t]+/([-*0-9a-zA-Z, ']*)/" err_t_tt ${errln})
+  string(REGEX MATCH "([E|G]+) ([-0-9a-zA-Z, ']*)" err_t_tt "${CMAKE_MATCH_3}")
+  string(STRIP "${CMAKE_MATCH_2}" err_t_tt)
+  string(APPEND zip_err_str "    { ${CMAKE_MATCH_1}, \"${err_t_tt}\" },\n")
+endforeach()
+string(APPEND zip_err_str [=[}\;
+
+const int _zip_err_details_count = sizeof(_zip_err_details)/sizeof(_zip_err_details[0])\;
+]=])
 file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/zip_err_str.c ${zip_err_str})
diff --git a/lib/zip.h b/lib/zip.h
index 4a1bf89..3b779f0 100644
--- a/lib/zip.h
+++ b/lib/zip.h
@@ -123,7 +123,7 @@
 #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 */
-#define ZIP_ER_INCONS 21          /* N Zip archive inconsistent */
+#define ZIP_ER_INCONS 21          /* L Zip archive inconsistent */
 #define ZIP_ER_REMOVE 22          /* S Can't remove file */
 #define ZIP_ER_DELETED 23         /* N Entry has been deleted */
 #define ZIP_ER_ENCRNOTSUPP 24     /* N Encryption method not supported */
@@ -138,9 +138,10 @@
 
 /* type of system error value */
 
-#define ZIP_ET_NONE 0 /* sys_err unused */
-#define ZIP_ET_SYS 1  /* sys_err is errno */
-#define ZIP_ET_ZLIB 2 /* sys_err is zlib error code */
+#define ZIP_ET_NONE 0   /* sys_err unused */
+#define ZIP_ET_SYS 1    /* sys_err is errno */
+#define ZIP_ET_ZLIB 2   /* sys_err is zlib error code */
+#define ZIP_ET_LIBZIP 3 /* sys_err is libzip error code */
 
 /* compression methods */
 
diff --git a/lib/zip_error.c b/lib/zip_error.c
index 9381d5b..f639f00 100644
--- a/lib/zip_error.c
+++ b/lib/zip_error.c
@@ -67,13 +67,14 @@
     zip_error_init(error);
     error->zip_err = ze;
     switch (zip_error_system_type(error)) {
-    case ZIP_ET_SYS:
-        error->sys_err = errno;
-        break;
-
-    default:
-        error->sys_err = 0;
-        break;
+        case ZIP_ET_SYS:
+        case ZIP_ET_LIBZIP:
+            error->sys_err = errno;
+            break;
+            
+        default:
+            error->sys_err = 0;
+            break;
     }
 }
 
diff --git a/lib/zip_error_strerror.c b/lib/zip_error_strerror.c
index d20b3b2..8b07131 100644
--- a/lib/zip_error_strerror.c
+++ b/lib/zip_error_strerror.c
@@ -39,43 +39,68 @@
 
 #include "zipint.h"
 
-
 ZIP_EXTERN const char *
 zip_error_strerror(zip_error_t *err) {
-    const char *zs, *ss;
+    const char *zip_error_string, *system_error_string;
     char buf[128], *s;
 
     zip_error_fini(err);
 
     if (err->zip_err < 0 || err->zip_err >= _zip_nerr_str) {
-        sprintf(buf, "Unknown error %d", err->zip_err);
-        zs = NULL;
-        ss = buf;
+        snprintf(buf, sizeof(buf), "Unknown error %d", err->zip_err);
+        buf[sizeof(buf) - 1] = '\0'; /* make sure string is NUL-terminated */
+        zip_error_string = NULL;
+        system_error_string = buf;
     }
     else {
-        zs = _zip_err_str[err->zip_err];
+        zip_error_string = _zip_err_str[err->zip_err];
 
         switch (_zip_err_type[err->zip_err]) {
-        case ZIP_ET_SYS:
-            ss = strerror(err->sys_err);
-            break;
-
-        case ZIP_ET_ZLIB:
-            ss = zError(err->sys_err);
-            break;
-
-        default:
-            ss = NULL;
+            case ZIP_ET_SYS:
+                system_error_string = strerror(err->sys_err);
+                break;
+                
+            case ZIP_ET_ZLIB:
+                system_error_string = zError(err->sys_err);
+                break;
+                
+            case ZIP_ET_LIBZIP: {
+                zip_uint8_t error = GET_ERROR_FROM_DETAIL(err->sys_err);
+                int index = GET_INDEX_FROM_DETAIL(err->sys_err);
+                
+                if (error == 0) {
+                    system_error_string = NULL;
+                }
+                else if (error >= _zip_err_details_count) {
+                    snprintf(buf, sizeof(buf), "invalid detail error %u", error);
+                    buf[sizeof(buf) - 1] = '\0'; /* make sure string is NUL-terminated */
+                    system_error_string = buf;
+                }
+                else if (_zip_err_details[error].has_index && index < MAX_DETAIL_INDEX) {
+                    snprintf(buf, sizeof(buf), "entry %d: %s", index, _zip_err_details[error].description);
+                    buf[sizeof(buf) - 1] = '\0'; /* make sure string is NUL-terminated */
+                    system_error_string = buf;
+                }
+                else {
+                    system_error_string = _zip_err_details[error].description;
+                }
+                break;
+            }
+                
+            default:
+                system_error_string = NULL;
         }
     }
 
-    if (ss == NULL)
-        return zs;
+    if (system_error_string == NULL) {
+        return zip_error_string;
+    }
     else {
-        if ((s = (char *)malloc(strlen(ss) + (zs ? strlen(zs) + 2 : 0) + 1)) == NULL)
+        if ((s = (char *)malloc(strlen(system_error_string) + (zip_error_string ? strlen(zip_error_string) + 2 : 0) + 1)) == NULL) {
             return _zip_err_str[ZIP_ER_MEMORY];
+        }
 
-        sprintf(s, "%s%s%s", (zs ? zs : ""), (zs ? ": " : ""), ss);
+        sprintf(s, "%s%s%s", (zip_error_string ? zip_error_string : ""), (zip_error_string ? ": " : ""), system_error_string);
         err->str = s;
 
         return s;
diff --git a/lib/zip_open.c b/lib/zip_open.c
index 292bfc8..1f1f1dc 100644
--- a/lib/zip_open.c
+++ b/lib/zip_open.c
@@ -224,8 +224,14 @@
 _zip_set_open_error(int *zep, const zip_error_t *err, int ze) {
     if (err) {
         ze = zip_error_code_zip(err);
-        if (zip_error_system_type(err) == ZIP_ET_SYS) {
-            errno = zip_error_code_system(err);
+	switch (zip_error_system_type(err)) {
+	    case ZIP_ET_SYS:
+	    case ZIP_ET_LIBZIP:
+		errno = zip_error_code_system(err);
+		break;
+		
+	    default:
+		break;
         }
     }
 
@@ -277,7 +283,7 @@
 
     if (cd->offset + cd->size > buf_offset + eocd_offset) {
         /* cdir spans past EOCD record */
-        zip_error_set(error, ZIP_ER_INCONS, 0);
+        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_OVERLAPS_EOCD);
         _zip_cdir_free(cd);
         return NULL;
     }
@@ -289,7 +295,7 @@
         tail_len = _zip_buffer_left(buffer);
 
         if (tail_len < comment_len || ((za->open_flags & ZIP_CHECKCONS) && tail_len != comment_len)) {
-            zip_error_set(error, ZIP_ER_INCONS, 0);
+            zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_COMMENT_LENGTH_INVALID);
             _zip_cdir_free(cd);
             return NULL;
         }
@@ -308,7 +314,7 @@
         _zip_buffer_set_offset(buffer, cd->offset - buf_offset);
 
         if ((data = _zip_buffer_get(buffer, cd->size)) == NULL) {
-            zip_error_set(error, ZIP_ER_INCONS, 0);
+            zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_LENGTH_INVALID);
             _zip_cdir_free(cd);
             return NULL;
         }
@@ -359,7 +365,7 @@
 
         if ((cd->entry[i].orig = _zip_dirent_new()) == NULL || (entry_size = _zip_dirent_read(cd->entry[i].orig, za->src, cd_buffer, false, error)) < 0) {
             if (grown && zip_error_code_zip(error) == ZIP_ER_NOZIP) {
-                zip_error_set(error, ZIP_ER_INCONS, 0);
+                zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(i, ZIP_ER_DETAIL_CDIR_ENTRY_INVALID));
             }
             _zip_cdir_free(cd);
             _zip_buffer_free(cd_buffer);
@@ -370,7 +376,7 @@
     }
 
     if (i != cd->nentry || left > 0) {
-        zip_error_set(error, ZIP_ER_INCONS, 0);
+        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_WRONG_ENTRIES_COUNT);
         _zip_buffer_free(cd_buffer);
         _zip_cdir_free(cd);
         return NULL;
@@ -394,7 +400,7 @@
         }
 
         if (!ok) {
-            zip_error_set(error, ZIP_ER_INCONS, 0);
+            zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_LENGTH_INVALID);
             _zip_buffer_free(cd_buffer);
             _zip_cdir_free(cd);
             return NULL;
@@ -453,7 +459,7 @@
         }
 
         if (_zip_headercomp(cd->entry[i].orig, &temp) != 0) {
-            zip_error_set(error, ZIP_ER_INCONS, 0);
+            zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(ZIP_ER_DETAIL_ENTRY_HEADER_MISMATCH, i));
             _zip_dirent_finalize(&temp);
             return -1;
         }
@@ -644,7 +650,7 @@
     zip_uint64_t i, nentry, size, offset, eocd_offset;
 
     if (_zip_buffer_left(buffer) < EOCDLEN) {
-        zip_error_set(error, ZIP_ER_INCONS, 0);
+        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD_LENGTH_INVALID);
         return NULL;
     }
 
@@ -677,12 +683,12 @@
 
     if (offset + size > buf_offset + eocd_offset) {
         /* cdir spans past EOCD record */
-        zip_error_set(error, ZIP_ER_INCONS, 0);
+        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_OVERLAPS_EOCD);
         return NULL;
     }
 
     if ((flags & ZIP_CHECKCONS) && offset + size != buf_offset + eocd_offset) {
-        zip_error_set(error, ZIP_ER_INCONS, 0);
+        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_LENGTH_INVALID);
         return NULL;
     }
 
@@ -723,7 +729,7 @@
 
     /* does EOCD fit before EOCD locator? */
     if (eocd_offset + EOCD64LEN > eocdloc_offset + buf_offset) {
-        zip_error_set(error, ZIP_ER_INCONS, 0);
+        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_OVERLAPS_EOCD);
         return NULL;
     }
 
@@ -744,7 +750,7 @@
     }
 
     if (memcmp(_zip_buffer_get(buffer, 4), EOCD64_MAGIC, 4) != 0) {
-        zip_error_set(error, ZIP_ER_INCONS, 0);
+        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_WRONG_MAGIC);
         if (free_buffer) {
             _zip_buffer_free(buffer);
         }
@@ -756,7 +762,7 @@
 
     /* is there a hole between EOCD and EOCD locator, or do they overlap? */
     if ((flags & ZIP_CHECKCONS) && size + eocd_offset + 12 != buf_offset + eocdloc_offset) {
-        zip_error_set(error, ZIP_ER_INCONS, 0);
+        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_OVERLAPS_EOCD);
         if (free_buffer) {
             _zip_buffer_free(buffer);
         }
@@ -778,7 +784,7 @@
         eocd_disk = eocd_disk64;
     }
     if ((flags & ZIP_CHECKCONS) && (eocd_disk != eocd_disk64 || num_disks != num_disks64)) {
-        zip_error_set(error, ZIP_ER_INCONS, 0);
+        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_MISMATCH);
         if (free_buffer) {
             _zip_buffer_free(buffer);
         }
@@ -825,16 +831,16 @@
     }
     if (offset + size > buf_offset + eocd_offset) {
         /* cdir spans past EOCD record */
-        zip_error_set(error, ZIP_ER_INCONS, 0);
+        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_OVERLAPS_EOCD);
         return NULL;
     }
     if ((flags & ZIP_CHECKCONS) && offset + size != buf_offset + eocd_offset) {
-        zip_error_set(error, ZIP_ER_INCONS, 0);
+        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_OVERLAPS_EOCD);
         return NULL;
     }
 
     if (nentry > size / CDENTRYSIZE) {
-        zip_error_set(error, ZIP_ER_INCONS, 0);
+        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_INVALID);
         return NULL;
     }
 
diff --git a/lib/zipint.h b/lib/zipint.h
index 6036d2e..47b48c5 100644
--- a/lib/zipint.h
+++ b/lib/zipint.h
@@ -189,6 +189,33 @@
 
 enum zip_les { ZIP_LES_NONE, ZIP_LES_UPPER, ZIP_LES_LOWER, ZIP_LES_INVAL };
 
+struct _zip_err_detail {
+    bool has_index;
+    const char *description;
+};
+
+extern const struct _zip_err_detail _zip_err_details[];
+extern const int _zip_err_details_count;
+
+/* macros for libzip-internal errors */
+#define MAX_DETAIL_INDEX 0xffffff
+#define MAKE_DETAIL_WITH_INDEX(error, index) ((((index) > MAX_DETAIL_INDEX) ? MAX_DETAIL_INDEX : (index)) << 8 | (error))
+#define GET_INDEX_FROM_DETAIL(error) (((error) >> 8) & MAX_DETAIL_INDEX)
+#define GET_ERROR_FROM_DETAIL(error) ((error) & 0xff)
+/* error code for libzip-internal errors */
+#define ZIP_ER_DETAIL_NO_DETAIL 0   /* G no detail */
+#define ZIP_ER_DETAIL_CDIR_OVERLAPS_EOCD 1  /* G central directory overlaps EOCD, or there is space between them */
+#define ZIP_ER_DETAIL_COMMENT_LENGTH_INVALID 2  /* G archive comment length incorrect */
+#define ZIP_ER_DETAIL_CDIR_LENGTH_INVALID 3  /* G central directory length invalid */
+#define ZIP_ER_DETAIL_CDIR_ENTRY_INVALID 4  /* E central header invalid */
+#define ZIP_ER_DETAIL_CDIR_WRONG_ENTRIES_COUNT 5  /* G central directory count of entries is incorrect */
+#define ZIP_ER_DETAIL_ENTRY_HEADER_MISMATCH 6  /* E local and central headers do not match */
+#define ZIP_ER_DETAIL_EOCD_LENGTH_INVALID 7  /* G wrong EOCD length */
+#define ZIP_ER_DETAIL_EOCD64_OVERLAPS_EOCD 8  /* G EOCD64 overlaps EOCD, or there is space between them */
+#define ZIP_ER_DETAIL_EOCD64_WRONG_MAGIC 9  /* G EOCD64 magic incorrect */
+#define ZIP_ER_DETAIL_EOCD64_MISMATCH 10  /* G EOCD64 and EOCD do not match */
+#define ZIP_ER_DETAIL_CDIR_INVALID 11  /* G invalid value in central directory */
+
 /* directory entry: general purpose bit flags */
 
 #define ZIP_GPBF_ENCRYPTED 0x0001u         /* is encrypted */