Call ZIP_SOURCE_SUPPORTS when creating a source, only call supported commands.
diff --git a/TODO b/TODO
index 80f0033..9a1cecb 100644
--- a/TODO
+++ b/TODO
@@ -241,7 +241,6 @@
 after merge:
 - direct reference to source and from upper layer are equivalent: only close/free if all are gone.
 - only allow multiple upper layers for sources that support seek
-- cache supported state in struct zip_source and check in _zip_source_call before calling
 D re-implement fix for OS X sandboxing (zip_archive_set_tempdir).
 - find out FSEEK_MAX/FTELL_MAX and use it
 - adapt man pages
diff --git a/lib/zip.h b/lib/zip.h
index c2b3032..5539930 100644
--- a/lib/zip.h
+++ b/lib/zip.h
@@ -231,6 +231,29 @@
 };
 typedef enum zip_source_cmd zip_source_cmd_t;
 
+#define ZIP_SOURCE_MAKE_COMMAND_BITMASK(cmd)    (1<<(cmd))
+
+#define ZIP_SOURCE_SUPPORTS_READABLE	(ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_OPEN) \
+                                         | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_READ) \
+                                         | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_CLOSE) \
+                                         | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_STAT) \
+                                         | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_ERROR) \
+                                         | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_FREE))
+
+#define ZIP_SOURCE_SUPPORTS_SEEKABLE	(ZIP_SOURCE_SUPPORTS_READABLE \
+                                         | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SEEK) \
+                                         | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_TELL) \
+                                         | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SUPPORTS))
+
+#define ZIP_SOURCE_SUPPORTS_WRITABLE    (ZIP_SOURCE_SUPPORTS_SEEKABLE \
+                                         | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE) \
+                                         | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_COMMIT_WRITE) \
+                                         | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_ROLLBACK_WRITE) \
+                                         | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_WRITE) \
+                                         | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SEEK_WRITE) \
+                                         | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_TELL_WRITE) \
+                                         | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_REMOVE))
+
 /* for use by sources */
 struct zip_source_args_seek {
     zip_int64_t offset;
diff --git a/lib/zip_source_call.c b/lib/zip_source_call.c
index ab14c76..21f28bc 100644
--- a/lib/zip_source_call.c
+++ b/lib/zip_source_call.c
@@ -39,6 +39,11 @@
 _zip_source_call(zip_source_t *src, void *data, zip_uint64_t length, zip_source_cmd_t command)
 {
     zip_int64_t ret;
+    
+    if ((src->supports & ZIP_SOURCE_MAKE_COMMAND_BITMASK(command)) == 0) {
+        zip_error_set(&src->error, ZIP_ER_OPNOTSUPP, 0);
+        return -1;
+    }
 
     if (src->src == NULL) {
         ret = src->cb.f(src->ud, data, length, command);
diff --git a/lib/zip_source_deflate.c b/lib/zip_source_deflate.c
index cea7880..a59ac49 100644
--- a/lib/zip_source_deflate.c
+++ b/lib/zip_source_deflate.c
@@ -356,6 +356,9 @@
 	deflate_free(ctx);
 	return 0;
 
+    case ZIP_SOURCE_SUPPORTS:
+        return ZIP_SOURCE_SUPPORTS_READABLE;
+            
     default:
         zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0);
 	return -1;
diff --git a/lib/zip_source_filep.c b/lib/zip_source_filep.c
index 3295fa0..a57b0b6 100644
--- a/lib/zip_source_filep.c
+++ b/lib/zip_source_filep.c
@@ -126,16 +126,16 @@
    
     zip_error_init(&ctx->error);
 
-    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);
+    ctx->supports = ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, 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);
+            ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE;
 	}
     }
     else if (fseek(ctx->f, 0, SEEK_CUR) == 0) {
-	ctx->supports |= zip_source_make_command_bitmap(ZIP_SOURCE_SEEK, -1);
+        ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE;
     }
 
     if ((zs=zip_source_function_create(read_file, ctx, error)) == NULL) {
diff --git a/lib/zip_source_function.c b/lib/zip_source_function.c
index d7e598c..1d4be93 100644
--- a/lib/zip_source_function.c
+++ b/lib/zip_source_function.c
@@ -59,6 +59,11 @@
     zs->cb.f = zcb;
     zs->ud = ud;
     
+    zs->supports = zcb(ud, NULL, 0, ZIP_SOURCE_SUPPORTS);
+    if (zs->supports < 0) {
+        zs->supports = ZIP_SOURCE_SUPPORTS_READABLE;
+    }
+    
     return zs;
 }
 
@@ -85,7 +90,7 @@
     src->ud = NULL;
     src->open_count = 0;
     src->write_state = ZIP_SOURCE_WRITE_CLOSED;
-    src->source_closed = 0;
+    src->source_closed = false;
     src->source_archive = NULL;
     src->refcount = 1;
     zip_error_init(&src->error);
diff --git a/lib/zip_source_layered.c b/lib/zip_source_layered.c
index 1f58148..94b3310 100644
--- a/lib/zip_source_layered.c
+++ b/lib/zip_source_layered.c
@@ -59,6 +59,11 @@
     zs->src = src;
     zs->cb.l = cb;
     zs->ud = ud;
-    
+
+    zs->supports = cb(src, ud, NULL, 0, ZIP_SOURCE_SUPPORTS);
+    if (zs->supports < 0) {
+        zs->supports = ZIP_SOURCE_SUPPORTS_READABLE;
+    }
+
     return zs;
 }
diff --git a/lib/zip_source_supports.c b/lib/zip_source_supports.c
index 13f4caf..3c100fb 100644
--- a/lib/zip_source_supports.c
+++ b/lib/zip_source_supports.c
@@ -40,15 +40,7 @@
 zip_int64_t
 zip_source_supports(zip_source_t *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;
+    return src->supports;
 }
 
 
@@ -58,7 +50,7 @@
     zip_int64_t bitmap;
     va_list ap;
     
-    bitmap = 1<<cmd0;
+    bitmap = ZIP_SOURCE_MAKE_COMMAND_BITMASK(cmd0);
     
     
     
@@ -68,7 +60,7 @@
         if (cmd < 0) {
             break;
         }
-        bitmap |= 1<<cmd;
+        bitmap |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(cmd);
     }
     va_end(ap);
     
diff --git a/lib/zip_source_window.c b/lib/zip_source_window.c
index a8801c7..69a0c91 100644
--- a/lib/zip_source_window.c
+++ b/lib/zip_source_window.c
@@ -44,7 +44,7 @@
     zip_stat_t stat;
     zip_error_t error;
     zip_int64_t supports;
-    int needs_seek;
+    bool needs_seek;
 };
 
 static zip_int64_t window_read(zip_source_t *, void *, void *, zip_uint64_t, zip_source_cmd_t);
@@ -76,8 +76,8 @@
     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)) ? 1 : 0;
+    ctx->supports = (zip_source_supports(src) & ZIP_SOURCE_SUPPORTS_SEEKABLE) | (zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1));
+    ctx->needs_seek = (ctx->supports & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SEEK)) ? true : false;
     
     if (st) {
         if (_zip_stat_merge(&ctx->stat, st, error) < 0) {
diff --git a/lib/zipint.h b/lib/zipint.h
index 51fdf38..bab9d43 100644
--- a/lib/zipint.h
+++ b/lib/zipint.h
@@ -363,9 +363,10 @@
     } cb;
     void *ud;
     zip_error_t error;
+    zip_int64_t supports;       /* supported commands */
     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 */
+    bool 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;
 };