libtiff: Rework TIFFOpenExt() and similar to use an opaque TIFFOpenOptions* opts argument, with alloc, free and setters

From 21d7dac23f44697c43fb9155c8e746678f0c2a68 Mon Sep 17 00:00:00 2001
From: Even Rouault <[EMAIL REDACTED]>
Date: Sun, 13 Nov 2022 16:23:21 +0100
Subject: [PATCH] Rework TIFFOpenExt() and similar to use an opaque
 TIFFOpenOptions* opts argument, with alloc, free and setters

---
 doc/functions/TIFFOpen.rst |  72 +++++++------------------
 libtiff/libtiff.def        |   4 ++
 libtiff/libtiff.map        |   4 ++
 libtiff/tif_open.c         |  85 ++++++++++++++++-------------
 libtiff/tif_unix.c         | 107 ++++++++-----------------------------
 libtiff/tif_win32.c        |  92 ++++++-------------------------
 libtiff/tiffio.h           |  46 +++++-----------
 libtiff/tiffiop.h          |   8 +++
 test/test_error_handlers.c |  14 +++--
 9 files changed, 142 insertions(+), 290 deletions(-)

diff --git a/doc/functions/TIFFOpen.rst b/doc/functions/TIFFOpen.rst
index 7d96161f..b3c5b25d 100644
--- a/doc/functions/TIFFOpen.rst
+++ b/doc/functions/TIFFOpen.rst
@@ -35,51 +35,21 @@ Synopsis
 
 .. c:function:: thandle_t TIFFSetClientdata(TIFF* tif, thandle_t newvalue)
 
-.. c:struct:: TIFFOpenExtStruct
+.. c:function:: TIFFOpenOptions* TIFFOpenOptionsAlloc(void)
 
-      .. c:var:: int version
+.. c:function:: void TIFFOpenOptionsFree(TIFFOpenOptions*);
 
-      .. c:var:: TIFFErrorHandlerExtR errorhandler
+.. c:function:: void TIFFOpenOptionsSetErrorHandlerExtR(TIFFOpenOptions* opts, TIFFErrorHandlerExtR handler, void* errorhandler_user_data)
 
-      .. c:var:: void* errorhandler_user_data
+.. c:function:: void TIFFOpenOptionsSetWarningHandlerExtR(TIFFOpenOptions* opts, TIFFErrorHandlerExtR handler, void* warnhandler_user_data)
 
-      .. c:var:: TIFFErrorHandlerExtR warnhandler
+.. c:function:: TIFF* TIFFOpenExt(const char* filename, const char* mode, TIFFOpenOptions* opts)
 
-      .. c:var:: void* warnhandler_user_data
+.. c:function:: TIFF* TIFFOpenWExt(const wchar_t* name, const char* mode, TIFFOpenOptions* opts)
 
-.. c:function:: TIFF* TIFFOpenExt(const char* filename, const char* mode, TIFFOpenExtStruct* arguments)
+.. c:function:: TIFF* TIFFFdOpenExt(const int fd, const char* filename, const char*mode, TIFFOpenOptions* opts)
 
-.. c:function:: TIFF* TIFFOpenWExt(const wchar_t* name, const char* mode, TIFFOpenExtStruct* arguments)
-
-.. c:function:: TIFF* TIFFFdOpenExt(const int fd, const char* filename, const char*mode, TIFFOpenExtStruct* arguments)
-
-.. c:struct:: TIFFClientOpenExtStruct
-
-      .. c:var:: int version
-
-      .. c:var:: TIFFReadWriteProc readproc
-
-      .. c:var:: TIFFReadWriteProc writeproc
-
-      .. c:var:: TIFFSeekProc seekproc
-
-      .. c:var:: TIFFCloseProc closeproc
-
-      .. c:var:: TIFFSizeProc sizeproc
-
-      .. c:var:: TIFFMapFileProc mapproc
-
-      .. c:var:: TIFFUnmapFileProc unmapproc
-
-      .. c:var:: TIFFErrorHandlerExtR errorhandler
-
-      .. c:var:: void* errorhandler_user_data
-
-      .. c:var:: TIFFErrorHandlerExtR warnhandler
-
-      .. c:var:: void* warnhandler_user_data
-
-.. c:function:: TIFF* TIFFClientOpenExt(const char* filename, const char* mode, thandle_t clientdata, TIFFOpenExtStruct* arguments)
+.. c:function:: TIFF* TIFFClientOpenExt(const char* filename, const char* mode, thandle_t clientdata, TIFFReadWriteProc readproc, TIFFReadWriteProc writeproc, TIFFSeekProc seekproc, TIFFCloseProc closeproc, TIFFSizeProc sizeproc, TIFFMapFileProc mapproc, TIFFUnmapFileProc unmapproc, TIFFOpenOptions* opts)
 
 Description
 -----------
@@ -132,17 +102,17 @@ first :c:func:`TIFFCleanup` should be called to free the internal
 TIFF structure without closing the file handle and afterwards the
 file should be closed using its file descriptor *fd*.
 
-:c:func:`TIFFOpenExt` (added in libtiff 4.5) is like :c:func:`TIFFOpen`, but re-entrant
-error and warning handlers may be passed. The version member of the arguments
-structure should be set to 1 for now.
+:c:func:`TIFFOpenExt` (added in libtiff 4.5) is like :c:func:`TIFFOpen`, but options,
+such as re-entrant error and warning handlers may be passed. The opts argument
+may be NULL.
 
-:c:func:`TIFFOpenWExt` (added in libtiff 4.5) is like :c:func:`TIFFOpenW`, but re-entrant
-error and warning handlers may be passed. The version member of the arguments
-structure should be set to 1 for now.
+:c:func:`TIFFOpenWExt` (added in libtiff 4.5) is like :c:func:`TIFFOpenW`, but options,
+such as re-entrant error and warning handlers may be passed. The opts argument
+may be NULL.
 
-:c:func:`TIFFFdOpenExt` (added in libtiff 4.5) is like :c:func:`TIFFFdOpen`, but re-entrant
-error and warning handlers may be passed. The version member of the arguments
-structure should be set to 1 for now.
+:c:func:`TIFFFdOpenExt` (added in libtiff 4.5) is like :c:func:`TIFFFdOpen`, but options,
+such as re-entrant error and warning handlers may be passed. The opts argument
+may be NULL.
 
 :c:func:`TIFFSetFileName` sets the file name in the tif-structure
 and returns the old file name.
@@ -181,11 +151,9 @@ The clientdata is used as open file's I/O descriptor within ``libtiff``.
   When updating the file's clientdata with :c:func:`TIFFSetClientdata`,
   the *fd* value is **not** updated.
 
-:c:func:`TIFFClientOpenExt` (added in libtiff 4.5) is like :c:func:`TIFFClientOpen`, but re-entrant
-error and warning handlers may be passed, as well as their respective user_data.
-The version member of the arguments structure should be set to 1 for now.
-
-:c:func:`TIFFClientdata` returns open file's clientdata handle.
+:c:func:`TIFFClientOpenExt` (added in libtiff 4.5) is like :c:func:`TIFFClientOpen`, but options,
+such as re-entrant error and warning handlers may be passed. The opts argument
+may be NULL.
 
 Options
 -------
diff --git a/libtiff/libtiff.def b/libtiff/libtiff.def
index 2555a176..8ec0e031 100644
--- a/libtiff/libtiff.def
+++ b/libtiff/libtiff.def
@@ -83,6 +83,10 @@ EXPORTS	TIFFAccessTagMethods
 	TIFFOpenExt
 	TIFFOpenW
 	TIFFOpenWExt
+	TIFFOpenOptionsAlloc
+	TIFFOpenOptionsFree
+	TIFFOpenOptionsSetErrorHandlerExtR
+	TIFFOpenOptionsSetWarningHandlerExtR
 	TIFFPrintDirectory
 	TIFFRGBAImageBegin
 	TIFFRGBAImageEnd
diff --git a/libtiff/libtiff.map b/libtiff/libtiff.map
index 39afafac..e9523352 100644
--- a/libtiff/libtiff.map
+++ b/libtiff/libtiff.map
@@ -210,4 +210,8 @@ LIBTIFF_4.5 {
     TIFFWarningExtR;
     TIFFSetErrorHandlerExtR;
     TIFFSetWarningHandlerExtR;
+    TIFFOpenOptionsAlloc;
+    TIFFOpenOptionsFree;
+    TIFFOpenOptionsSetErrorHandlerExtR;
+    TIFFOpenOptionsSetWarningHandlerExtR;
 } LIBTIFF_4.4;
diff --git a/libtiff/tif_open.c b/libtiff/tif_open.c
index f95f5163..96d4b131 100644
--- a/libtiff/tif_open.c
+++ b/libtiff/tif_open.c
@@ -67,6 +67,29 @@ _TIFFgetMode(const char* mode, const char* module)
 	return (m);
 }
 
+TIFFOpenOptions* TIFFOpenOptionsAlloc()
+{
+    TIFFOpenOptions* opts = (TIFFOpenOptions*)_TIFFcalloc(1, sizeof(TIFFOpenOptions));
+    return opts;
+}
+
+void TIFFOpenOptionsFree(TIFFOpenOptions* opts)
+{
+    _TIFFfree(opts);
+}
+
+void TIFFOpenOptionsSetErrorHandlerExtR(TIFFOpenOptions* opts, TIFFErrorHandlerExtR handler, void* errorhandler_user_data)
+{
+    opts->errorhandler = handler;
+    opts->errorhandler_user_data = errorhandler_user_data;
+}
+
+void TIFFOpenOptionsSetWarningHandlerExtR(TIFFOpenOptions* opts, TIFFErrorHandlerExtR handler, void* warnhandler_user_data)
+{
+    opts->warnhandler = handler;
+    opts->warnhandler_user_data = warnhandler_user_data;
+}
+
 TIFF*
 TIFFClientOpen(
 	const char* name, const char* mode,
@@ -79,52 +102,35 @@ TIFFClientOpen(
 	TIFFMapFileProc mapproc,
 	TIFFUnmapFileProc unmapproc
 ) {
-  TIFFClientOpenExtStruct arguments = {
-      .version = 1,
-      .readproc = readproc,
-      .writeproc = writeproc,
-      .seekproc = seekproc,
-      .closeproc = closeproc,
-      .sizeproc = sizeproc,
-      .mapproc = mapproc,
-      .unmapproc = unmapproc,
-      .errorhandler = NULL,
-      .errorhandler_user_data = NULL,
-      .warnhandler = NULL,
-      .warnhandler_user_data = NULL
-  };
-  return TIFFClientOpenExt(name, mode, clientdata, &arguments);
+  return TIFFClientOpenExt(name, mode, clientdata,
+                           readproc,
+                           writeproc,
+                           seekproc,
+                           closeproc,
+                           sizeproc,
+                           mapproc,
+                           unmapproc,
+                           NULL);
 }
 
 TIFF*
 TIFFClientOpenExt(
 	const char* name, const char* mode,
 	thandle_t clientdata,
-    TIFFClientOpenExtStruct* arguments)
+	TIFFReadWriteProc readproc,
+	TIFFReadWriteProc writeproc,
+	TIFFSeekProc seekproc,
+	TIFFCloseProc closeproc,
+	TIFFSizeProc sizeproc,
+	TIFFMapFileProc mapproc,
+	TIFFUnmapFileProc unmapproc,
+    TIFFOpenOptions* opts)
 {
 	static const char module[] = "TIFFClientOpenExt";
 	TIFF *tif;
 	int m;
 	const char* cp;
 
-    if (arguments == NULL)
-    {
-        TIFFErrorExt(clientdata, module, "arguments should NOT be NULL");
-        return NULL;
-    }
-    if (arguments->version < 1)
-    {
-        TIFFErrorExt(clientdata, module, "arguments->version should be >= 1");
-        return NULL;
-    }
-    TIFFReadWriteProc readproc = arguments->readproc;
-    TIFFReadWriteProc writeproc = arguments->writeproc;
-    TIFFSeekProc seekproc = arguments->seekproc;
-    TIFFCloseProc closeproc = arguments->closeproc;
-    TIFFSizeProc sizeproc = arguments->sizeproc;
-    TIFFMapFileProc mapproc = arguments->mapproc;
-    TIFFUnmapFileProc unmapproc = arguments->unmapproc;
-
 	/* The following are configuration checks. They should be redundant, but should not
 	 * compile to any actual code in an optimised release build anyway. If any of them
 	 * fail, (makefile-based or other) configuration is not correct */
@@ -175,10 +181,13 @@ TIFFClientOpenExt(
 	tif->tif_sizeproc = sizeproc;
 	tif->tif_mapproc = mapproc ? mapproc : _tiffDummyMapProc;
 	tif->tif_unmapproc = unmapproc ? unmapproc : _tiffDummyUnmapProc;
-    tif->tif_errorhandler = arguments->errorhandler;
-    tif->tif_errorhandler_user_data = arguments->errorhandler_user_data;
-    tif->tif_warnhandler = arguments->warnhandler;
-    tif->tif_warnhandler_user_data = arguments->warnhandler_user_data;
+    if( opts )
+    {
+        tif->tif_errorhandler = opts->errorhandler;
+        tif->tif_errorhandler_user_data = opts->errorhandler_user_data;
+        tif->tif_warnhandler = opts->warnhandler;
+        tif->tif_warnhandler_user_data = opts->warnhandler_user_data;
+    }
 
 	if (!readproc || !writeproc || !seekproc || !closeproc || !sizeproc) {
 		TIFFErrorExtR(tif, module,
diff --git a/libtiff/tif_unix.c b/libtiff/tif_unix.c
index 34ad36c8..0d9fee33 100644
--- a/libtiff/tif_unix.c
+++ b/libtiff/tif_unix.c
@@ -202,54 +202,29 @@ _tiffUnmapProc(thandle_t fd, void* base, toff_t size)
 TIFF*
 TIFFFdOpen(int fd, const char* name, const char* mode)
 {
-    TIFFOpenExtStruct arguments = {
-      .version = 1,
-      .errorhandler = NULL,
-      .errorhandler_user_data = NULL,
-      .warnhandler = NULL,
-      .warnhandler_user_data = NULL
-    };
-    return TIFFFdOpenExt(fd, name, mode, &arguments);
+    return TIFFFdOpenExt(fd, name, mode, NULL);
 }
 
 TIFF*
-TIFFFdOpenExt(int fd, const char* name, const char* mode, TIFFOpenExtStruct* arguments)
+TIFFFdOpenExt(int fd, const char* name, const char* mode, TIFFOpenOptions* opts)
 {
-    static const char module[] = "TIFFFdOpenExt";
 	TIFF* tif;
 
-    if (arguments == NULL)
-    {
-        TIFFErrorExt(0, module, "arguments should NOT be NULL");
-        return NULL;
-    }
-    if (arguments->version < 1)
-    {
-        TIFFErrorExt(0, module, "arguments->version should be >= 1");
-        return NULL;
-    }
-    TIFFClientOpenExtStruct client_arguments = {
-        .version = 1,
-        .readproc = _tiffReadProc,
-        .writeproc = _tiffWriteProc,
-        .seekproc = _tiffSeekProc,
-        .closeproc = _tiffCloseProc,
-        .sizeproc = _tiffSizeProc,
-        .mapproc = _tiffMapProc,
-        .unmapproc = _tiffUnmapProc,
-        .errorhandler = arguments->errorhandler,
-        .errorhandler_user_data = arguments->errorhandler_user_data,
-        .warnhandler = arguments->warnhandler,
-        .warnhandler_user_data = arguments->warnhandler_user_data
-    };
-
-	fd_as_handle_union_t fdh;
-	fdh.fd = fd;
-	tif = TIFFClientOpenExt(name, mode,
-	    fdh.h, &client_arguments);
-	if (tif)
-		tif->tif_fd = fd;
-	return (tif);
+    fd_as_handle_union_t fdh;
+    fdh.fd = fd;
+    tif = TIFFClientOpenExt(name, mode,
+                            fdh.h,
+                            _tiffReadProc,
+                            _tiffWriteProc,
+                            _tiffSeekProc,
+                            _tiffCloseProc,
+                            _tiffSizeProc,
+                            _tiffMapProc,
+                            _tiffUnmapProc,
+                            opts);
+    if (tif)
+        tif->tif_fd = fd;
+    return (tif);
 }
 
 /*
@@ -258,34 +233,16 @@ TIFFFdOpenExt(int fd, const char* name, const char* mode, TIFFOpenExtStruct* arg
 TIFF*
 TIFFOpen(const char* name, const char* mode)
 {
-    TIFFOpenExtStruct arguments = {
-        .version = 1,
-        .errorhandler = NULL,
-        .errorhandler_user_data = NULL,
-        .warnhandler = NULL,
-        .warnhandler_user_data = NULL
-    };
-    return TIFFOpenExt(name, mode, &arguments);
+    return TIFFOpenExt(name, mode, NULL);
 }
 
 TIFF*
-TIFFOpenExt(const char* name, const char* mode, TIFFOpenExtStruct* arguments)
+TIFFOpenExt(const char* name, const char* mode, TIFFOpenOptions* opts)
 {
 	static const char module[] = "TIFFOpen";
 	int m, fd;
 	TIFF* tif;
 
-    if (arguments == NULL)
-    {
-        TIFFErrorExt(0, module, "arguments should NOT be NULL");
-        return NULL;
-    }
-    if (arguments->version < 1)
-    {
-        TIFFErrorExt(0, module, "arguments->version should be >= 1");
-        return NULL;
-    }
-
 	m = _TIFFgetMode(mode, module);
 	if (m == -1)
 		return ((TIFF*)0);
@@ -305,7 +262,7 @@ TIFFOpenExt(const char* name, const char* mode, TIFFOpenExtStruct* arguments)
 		return ((TIFF *)0);
 	}
 
-	tif = TIFFFdOpenExt((int)fd, name, mode, arguments);
+	tif = TIFFFdOpenExt((int)fd, name, mode, opts);
 	if(!tif)
 		close(fd);
 	return tif;
@@ -319,17 +276,10 @@ TIFFOpenExt(const char* name, const char* mode, TIFFOpenExtStruct* arguments)
 TIFF*
 TIFFOpenW(const wchar_t* name, const char* mode)
 {
-    TIFFOpenExtStruct arguments = {
-        .version = 1,
-        .errorhandler = NULL,
-        .errorhandler_user_data = NULL,
-        .warnhandler = NULL,
-        .warnhandler_user_data = NULL
-    };
-    return TIFFOpenWEx(name, mode, &arguments);
+    return TIFFOpenWEx(name, mode, NULL);
 }
 TIFF*
-TIFFOpenWExt(const wchar_t* name, const char* mode, TIFFOpenExtStruct* arguments)
+TIFFOpenWExt(const wchar_t* name, const char* mode, TIFFOpenOptions* opts)
 {
 	static const char module[] = "TIFFOpenW";
 	int m, fd;
@@ -337,17 +287,6 @@ TIFFOpenWExt(const wchar_t* name, const char* mode, TIFFOpenExtStruct* arguments
 	char *mbname;
 	TIFF* tif;
 
-    if (arguments == NULL)
-    {
-        TIFFErrorExt(0, module, "arguments should NOT be NULL");
-        return NULL;
-    }
-    if (arguments->version < 1)
-    {
-        TIFFErrorExt(0, module, "arguments->version should be >= 1");
-        return NULL;
-    }
-
 	m = _TIFFgetMode(mode, module);
 	if (m == -1)
 		return ((TIFF*)0);
@@ -378,7 +317,7 @@ TIFFOpenWExt(const wchar_t* name, const char* mode, TIFFOpenExtStruct* arguments
 	}
 
 	tif = TIFFFdOpenExt((int)fd, (mbname != NULL) ? mbname : "<unknown>",
-			 mode, arguments);
+			 mode, opts);
 	
 	_TIFFfree(mbname);
 	
diff --git a/libtiff/tif_win32.c b/libtiff/tif_win32.c
index 2e62f080..d5db8b7f 100644
--- a/libtiff/tif_win32.c
+++ b/libtiff/tif_win32.c
@@ -232,32 +232,16 @@ _tiffUnmapProc(thandle_t fd, void* base, toff_t size)
 TIFF*
 TIFFFdOpen(int ifd, const char* name, const char* mode)
 {
-    TIFFOpenExtStruct arguments;
-    arguments.version = 1;
-    arguments.errorhandler = NULL;
-    arguments.warnhandler = NULL;
-    return TIFFFdOpenExt(ifd, name, mode, &arguments);
+    return TIFFFdOpenExt(ifd, name, mode, NULL);
 }
 
 TIFF*
-TIFFFdOpenExt(int ifd, const char* name, const char* mode, TIFFOpenExtStruct* arguments)
+TIFFFdOpenExt(int ifd, const char* name, const char* mode, TIFFOpenOptions* opts)
 {
-    static const char module[] = "TIFFFdOpenExt";
 	TIFF* tif;
 	int fSuppressMap;
 	int m;
 
-    if( arguments == NULL )
-    {
-        TIFFErrorExt(0, module, "arguments should NOT be NULL");
-        return NULL;
-    }
-    if( arguments->version < 1 )
-    {
-        TIFFErrorExt(0, module, "arguments->version should be >= 1");
-        return NULL;
-    }
-
 	fSuppressMap=0;
 	for (m=0; mode[m]!=0; m++)
 	{
@@ -268,20 +252,15 @@ TIFFFdOpenExt(int ifd, const char* name, const char* mode, TIFFOpenExtStruct* ar
 		}
 	}
 
-    TIFFClientOpenExtStruct client_arguments;
-    client_arguments.version = 1;
-    client_arguments.readproc = _tiffReadProc;
-    client_arguments.writeproc = _tiffWriteProc;
-    client_arguments.seekproc = _tiffSeekProc;
-    client_arguments.closeproc = _tiffCloseProc;
-    client_arguments.sizeproc = _tiffSizeProc;
-    client_arguments.mapproc = fSuppressMap ? _tiffDummyMapProc : _tiffMapProc;
-    client_arguments.unmapproc = fSuppressMap ? _tiffDummyUnmapProc : _tiffUnmapProc;
-    client_arguments.errorhandler = arguments->errorhandler;
-    client_arguments.warnhandler = arguments->warnhandler;
-
-	tif = TIFFClientOpenExt(name, mode, thandle_from_int(ifd),
-			&client_arguments);
+    tif = TIFFClientOpenExt(name, mode, thandle_from_int(ifd),
+                            _tiffReadProc,
+                            _tiffWriteProc,
+                            _tiffSeekProc,
+                            _tiffCloseProc,
+                            _tiffSizeProc,
+                            fSuppressMap ? _tiffDummyMapProc : _tiffMapProc,
+                            fSuppressMap ? _tiffDummyUnmapProc : _tiffUnmapProc,
+                            opts);
 	if (tif)
 		tif->tif_fd = ifd;
 	return (tif);
@@ -295,18 +274,11 @@ TIFFFdOpenExt(int ifd, const char* name, const char* mode, TIFFOpenExtStruct* ar
 TIFF*
 TIFFOpen(const char* name, const char* mode)
 {
-    TIFFOpenExtStruct arguments = {
-        .version = 1,
-        .errorhandler = NULL,
-        .errorhandler_user_data = NULL,
-        .warnhandler = NULL,
-        .warnhandler_user_data = NULL
-    };
-    return TIFFOpenExt(name, mode, &arguments);
+    return TIFFOpenExt(name, mode, NULL);
 }
 
 TIFF*
-TIFFOpenExt(const char* name, const char* mode, TIFFOpenExtStruct* arguments)
+TIFFOpenExt(const char* name, const char* mode, TIFFOpenOptions* opts)
 {
 	static const char module[] = "TIFFOpen";
 	thandle_t fd;
@@ -314,17 +286,6 @@ TIFFOpenExt(const char* name, const char* mode, TIFFOpenExtStruct* arguments)
 	DWORD dwMode;
 	TIFF* tif;
 
-    if (arguments == NULL)
-    {
-        TIFFErrorExt(0, module, "arguments should NOT be NULL");
-        return NULL;
-    }
-    if (arguments->version < 1)
-    {
-        TIFFErrorExt(0, module, "arguments->version should be >= 1");
-        return NULL;
-    }
-
 	m = _TIFFgetMode(mode, module);
 
 	switch(m) {
@@ -346,7 +307,7 @@ TIFFOpenExt(const char* name, const char* mode, TIFFOpenExtStruct* arguments)
 		return ((TIFF *)0);
 	}
 
-	tif = TIFFFdOpenExt(thandle_to_int(fd), name, mode, arguments);
+	tif = TIFFFdOpenExt(thandle_to_int(fd), name, mode, opts);
 	if(!tif)
 		CloseHandle(fd);
 	return tif;
@@ -358,18 +319,11 @@ TIFFOpenExt(const char* name, const char* mode, TIFFOpenExtStruct* arguments)
 TIFF*
 TIFFOpenW(const wchar_t* name, const char* mode)
 {
-    TIFFOpenExtStruct arguments = {
-        .version = 1,
-        .errorhandler = NULL,
-        .errorhandler_user_data = NULL,
-        .warnhandler = NULL,
-        .warnhandler_user_data = NULL
-    };
-    return TIFFOpenWExt(name, mode, &arguments);
+    return TIFFOpenWExt(name, mode, NULL);
 }
 
 TIFF*
-TIFFOpenWExt(const wchar_t* name, const char* mode, TIFFOpenExtStruct* arguments)
+TIFFOpenWExt(const wchar_t* name, const char* mode, TIFFOpenOptions* opts)
 {
 	static const char module[] = "TIFFOpenW";
 	thandle_t fd;
@@ -379,17 +333,6 @@ TIFFOpenWExt(const wchar_t* name, const char* mode, TIFFOpenExtStruct* arguments
 	char *mbname;
 	TIFF *tif;
 
-    if (arguments == NULL)
-    {
-        TIFFErrorExt(0, module, "arguments should NOT be NULL");
-        return NULL;
-    }
-    if (arguments->version < 1)
-    {
-        TIFFErrorExt(0, module, "arguments->version should be >= 1");
-        return NULL;
-    }
-
 	m = _TIFFgetMode(mode, module);
 
 	switch(m) {
@@ -426,8 +369,7 @@ TIFFOpenWExt(const wchar_t* name, const char* mode, TIFFOpenExtStruct* arguments
 	}
 
 	tif = TIFFFdOpenExt(thandle_to_int(fd),
-			 (mbname != NULL) ? mbname : "<unknown>", mode,
-             arguments);
+			 (mbname != NULL) ? mbname : "<unknown>", mode, opts);
 	if(!tif)
 		CloseHandle(fd);
 
diff --git a/libtiff/tiffio.h b/libtiff/tiffio.h
index 36abdc4d..6523c048 100644
--- a/libtiff/tiffio.h
+++ b/libtiff/tiffio.h
@@ -465,53 +465,33 @@ extern void TIFFSetWarningHandlerExtR(TIFF*, TIFFErrorHandlerExtR, void* warnhan
 extern void TIFFWarningExtR(TIFF*, const char*, const char*, ...) TIFF_ATTRIBUTE((__format__ (__printf__,3,4)));
 extern void TIFFErrorExtR(TIFF*, const char*, const char*, ...) TIFF_ATTRIBUTE((__format__ (__printf__,3,4)));
 
-typedef struct TIFFOpenExtStruct
-{
-    int                  version; /* should be set to 1 for now. */
-
-    TIFFErrorHandlerExtR errorhandler; /* may be NULL */
-    void*                errorhandler_user_data; /* may be NULL */
-    TIFFErrorHandlerExtR warnhandler; /* may be NULL */
-    void*                warnhandler_user_data; /* may be NULL */
-    /* If new fields are added, they should be handled by nVersion == 2 */
-} TIFFOpenExtStruct;
+typedef struct TIFFOpenOptions TIFFOpenOptions;
+extern TIFFOpenOptions* TIFFOpenOptionsAlloc(void);
+extern void TIFFOpenOptionsFree(TIFFOpenOptions*);
+extern void TIFFOpenOptionsSetErrorHandlerExtR(TIFFOpenOptions* opts, TIFFErrorHandlerExtR handler, void* errorhandler_user_data);
+extern void TIFFOpenOptionsSetWarningHandlerExtR(TIFFOpenOptions* opts, TIFFErrorHandlerExtR handler, void* warnhandler_user_data);
 
 extern TIFF* TIFFOpen(const char*, const char*);
-extern TIFF* TIFFOpenExt(const char*, const char*, TIFFOpenExtStruct* arguments);
+extern TIFF* TIFFOpenExt(const char*, const char*, TIFFOpenOptions* opts);
 # ifdef __WIN32__
 extern TIFF* TIFFOpenW(const wchar_t*, const char*);
-extern TIFF* TIFFOpenWExt(const wchar_t*, const char*, TIFFOpenExtStruct* arguments);
+extern TIFF* TIFFOpenWExt(const wchar_t*, const char*, TIFFOpenOptions* opts);
 # endif /* __WIN32__ */
 extern TIFF* TIFFFdOpen(int, const char*, const char*);
-extern TIFF* TIFFFdOpenExt(int, const char*, const char*, TIFFOpenExtStruct* arguments);
+extern TIFF* TIFFFdOpenExt(int, const char*, const char*, TIFFOpenOptions* opts);
 extern TIFF* TIFFClientOpen(const char*, const char*,
 	    thandle_t,
 	    TIFFReadWriteProc, TIFFReadWriteProc,
 	    TIFFSeekProc, TIFFCloseProc,
 	    TIFFSizeProc,
 	    TIFFMapFileProc, TIFFUnmapFileProc);
-
-typedef struct TIFFClientOpenExtStruct
-{
-    int                  version; /* should be set to 1 for now. */
-
-    TIFFReadWriteProc    readproc; /* must *NOT* be NULL */
-    TIFFReadWriteProc    writeproc; /* must *NOT* be NULL */
-    TIFFSeekProc         seekproc; /* must *NOT* be NULL */
-    TIFFCloseProc        closeproc; /* must *NOT* be NULL */
-    TIFFSizeProc         sizeproc; /* must *NOT* be NULL */
-    TIFFMapFileProc      mapproc; /* may be NULL */
-    TIFFUnmapFileProc    unmapproc; /* may be NULL */
-    TIFFErrorHandlerExtR errorhandler; /* may be NULL */
-    void*                errorhandler_user_data; /* may be NULL */
-    TIFFErrorHandlerExtR warnhandler; /* may be NULL */
-    void*                warnhandler_user_data; /* may be NULL */
-    /* If new fields are added, they should be handled by nVersion == 2 */
-} TIFFClientOpenExtStruct;
-
 extern TIFF* TIFFClientOpenExt(const char*, const char*,
                                thandle_t,
-                               TIFFClientOpenExtStruct* arguments);
+                               TIFFReadWriteProc, TIFFReadWriteProc,
+                               TIFFSeekProc, TIFFCloseProc,
+                               TIFFSizeProc,
+                               TIFFMapFileProc, TIFFUnmapFileProc,
+                               TIFFOpenOptions* opts);
 extern TIFFExtendProc TIFFSetTagExtender(TIFFExtendProc);
 extern uint32_t TIFFComputeTile(TIFF* tif, uint32_t x, uint32_t y, uint32_t z, uint16_t s);
 extern int TIFFCheckTile(TIFF* tif, uint32_t x, uint32_t y, uint32_t z, uint16_t s);
diff --git a/libtiff/tiffiop.h b/libtiff/tiffiop.h
index 7fbeef4b..4d703edd 100644
--- a/libtiff/tiffiop.h
+++ b/libtiff/tiffiop.h
@@ -204,6 +204,14 @@ struct tiff {
     void*                 tif_warnhandler_user_data;
 };
 
+struct TIFFOpenOptions
+{
+    TIFFErrorHandlerExtR errorhandler; /* may be NULL */
+    void*                errorhandler_user_data; /* may be NULL */
+    TIFFErrorHandlerExtR warnhandler; /* may be NULL */
+    void*                warnhandler_user_data; /* may be NULL */
+};
+
 #define isPseudoTag(t) (t > 0xffff)            /* is tag value normal or pseudo */
 
 #define isTiled(tif) (((tif)->tif_flags & TIFF_ISTILED) != 0)
diff --git a/test/test_error_handlers.c b/test/test_error_handlers.c
index 56faa29e..d1ba0587 100644
--- a/test/test_error_handlers.c
+++ b/test/test_error_handlers.c
@@ -80,14 +80,12 @@ int test_open_ext(int handlers_set_in_open)
     TIFF* tif;
     if( handlers_set_in_open )
     {
-        TIFFOpenExtStruct arguments = {
-            .version = 1,
-            .errorhandler = myErrorHandler,
-            .errorhandler_user_data = &errorhandler_user_data,
-            .warnhandler = myErrorHandler,
-            .warnhandler_user_data = &warnhandler_user_data
-        };
-        tif = TIFFOpenExt("test_error_handler.tif", "w", &arguments);
+        TIFFOpenOptions* opts = TIFFOpenOptionsAlloc();
+        assert(opts);
+        TIFFOpenOptionsSetErrorHandlerExtR(opts, myErrorHandler, &errorhandler_user_data);
+        TIFFOpenOptionsSetWarningHandlerExtR(opts, myErrorHandler, &warnhandler_user_data);
+        tif = TIFFOpenExt("test_error_handler.tif", "w", opts);
+        TIFFOpenOptionsFree(opts);
     }
     else
     {