libtiff: Add a _TIFFErrorEarly() function to be able to use the re-entrant error handler, even before TIFF* is valid

From 73d34370e1728fec96c4c0d3fe527aa2f49f3e1e Mon Sep 17 00:00:00 2001
From: Even Rouault <[EMAIL REDACTED]>
Date: Sun, 13 Nov 2022 20:55:51 +0100
Subject: [PATCH] Add a _TIFFErrorEarly() function to be able to use the
 re-entrant error handler, even before TIFF* is valid

---
 doc/functions/TIFFOpen.rst |  8 ++++++++
 libtiff/tif_error.c        | 22 ++++++++++++++++++++++
 libtiff/tif_open.c         |  8 ++++----
 libtiff/tif_unix.c         | 10 +++++-----
 libtiff/tif_win32.c        | 10 +++++-----
 libtiff/tiffiop.h          |  3 ++-
 6 files changed, 46 insertions(+), 15 deletions(-)

diff --git a/doc/functions/TIFFOpen.rst b/doc/functions/TIFFOpen.rst
index b3c5b25d..7566ef62 100644
--- a/doc/functions/TIFFOpen.rst
+++ b/doc/functions/TIFFOpen.rst
@@ -104,14 +104,20 @@ file should be closed using its file descriptor *fd*.
 
 :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. Note that in the early stages of the execution of the function,
+the TIFF* argument passed to the re-entrant error handler (specified in opts)
 may be NULL.
 
 :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. Note that in the early stages of the execution of the function,
+the TIFF* argument passed to the re-entrant error handler (specified in opts)
 may be NULL.
 
 :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. Note that in the early stages of the execution of the function,
+the TIFF* argument passed to the re-entrant error handler (specified in opts)
 may be NULL.
 
 :c:func:`TIFFSetFileName` sets the file name in the tif-structure
@@ -153,6 +159,8 @@ The clientdata is used as open file's I/O descriptor within ``libtiff``.
 
 :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. Note that in the early stages of the execution of the function,
+the TIFF* argument passed to the re-entrant error handler (specified in opts)
 may be NULL.
 
 Options
diff --git a/libtiff/tif_error.c b/libtiff/tif_error.c
index 000654e0..ed8e1de4 100644
--- a/libtiff/tif_error.c
+++ b/libtiff/tif_error.c
@@ -84,6 +84,28 @@ void TIFFSetErrorHandlerExtR(TIFF* tif, TIFFErrorHandlerExtR handler, void* erro
   }
 }
 
+void _TIFFErrorEarly(TIFFOpenOptions* opts, thandle_t clientdata, const char* module, const char* fmt, ...)
+{
+    va_list ap;
+    if (opts && opts->errorhandler) {
+        va_start(ap, fmt);
+        int stop = opts->errorhandler(NULL, opts->errorhandler_user_data, module, fmt, ap);
+        va_end(ap);
+        if (stop) return;
+    }
+    if (_TIFFerrorHandler) {
+        va_start(ap, fmt);
+        (*_TIFFerrorHandler)(module, fmt, ap);
+        va_end(ap);
+    }
+    if (_TIFFerrorHandlerExt) {
+        va_start(ap, fmt);
+        (*_TIFFerrorHandlerExt)(clientdata, module, fmt, ap);
+        va_end(ap);
+    }
+}
+
+
 void TIFFErrorExtR(TIFF* tif, const char* module, const char* fmt, ...)
 {
 	va_list ap;
diff --git a/libtiff/tif_open.c b/libtiff/tif_open.c
index 96d4b131..23e206d7 100644
--- a/libtiff/tif_open.c
+++ b/libtiff/tif_open.c
@@ -44,7 +44,7 @@ _tiffDummyUnmapProc(thandle_t fd, void* base, toff_t size)
 }
 
 int
-_TIFFgetMode(const char* mode, const char* module)
+_TIFFgetMode(TIFFOpenOptions* opts, thandle_t clientdata, const char* mode, const char* module)
 {
 	int m = -1;
 
@@ -61,7 +61,7 @@ _TIFFgetMode(const char* mode, const char* module)
 			m |= O_TRUNC;
 		break;
 	default:
-		TIFFErrorExt(0, module, "\"%s\": Bad mode", mode);
+		_TIFFErrorEarly(opts, clientdata, module, "\"%s\": Bad mode", mode);
 		break;
 	}
 	return (m);
@@ -157,12 +157,12 @@ TIFFClientOpenExt(
 		#endif
 	}
 
-	m = _TIFFgetMode(mode, module);
+	m = _TIFFgetMode(opts, clientdata, mode, module);
 	if (m == -1)
 		goto bad2;
 	tif = (TIFF *)_TIFFmalloc((tmsize_t)(sizeof (TIFF) + strlen(name) + 1));
 	if (tif == NULL) {
-		TIFFErrorExt(clientdata, module, "%s: Out of memory (TIFF structure)", name);
+		_TIFFErrorEarly(opts, clientdata, module, "%s: Out of memory (TIFF structure)", name);
 		goto bad2;
 	}
 	_TIFFmemset(tif, 0, sizeof (*tif));
diff --git a/libtiff/tif_unix.c b/libtiff/tif_unix.c
index 0d9fee33..89ae0c17 100644
--- a/libtiff/tif_unix.c
+++ b/libtiff/tif_unix.c
@@ -243,7 +243,7 @@ TIFFOpenExt(const char* name, const char* mode, TIFFOpenOptions* opts)
 	int m, fd;
 	TIFF* tif;
 
-	m = _TIFFgetMode(mode, module);
+	m = _TIFFgetMode(opts, NULL, mode, module);
 	if (m == -1)
 		return ((TIFF*)0);
 
@@ -255,9 +255,9 @@ TIFFOpenExt(const char* name, const char* mode, TIFFOpenOptions* opts)
 	fd = open(name, m, 0666);
 	if (fd < 0) {
 		if (errno > 0 && strerror(errno) != NULL ) {
-			TIFFErrorExt(0, module, "%s: %s", name, strerror(errno) );
+			_TIFFErrorEarly(opts, NULL, module, "%s: %s", name, strerror(errno) );
 		} else {
-			TIFFErrorExt(0, module, "%s: Cannot open", name);
+			_TIFFErrorEarly(opts, NULL, module, "%s: Cannot open", name);
 		}
 		return ((TIFF *)0);
 	}
@@ -298,7 +298,7 @@ TIFFOpenWExt(const wchar_t* name, const char* mode, TIFFOpenOptions* opts)
 
 	fd = _wopen(name, m, 0666);
 	if (fd < 0) {
-		TIFFErrorExt(0, module, "%ls: Cannot open", name);
+		_TIFFErrorEarly(opts, NULL, module, "%ls: Cannot open", name);
 		return ((TIFF *)0);
 	}
 
@@ -307,7 +307,7 @@ TIFFOpenWExt(const wchar_t* name, const char* mode, TIFFOpenOptions* opts)
 	if (mbsize > 0) {
 		mbname = _TIFFmalloc(mbsize);
 		if (!mbname) {
-			TIFFErrorExt(0, module,
+			_TIFFErrorEarly(opts, NULL, module,
 			"Can't allocate space for filename conversion buffer");
 			return ((TIFF*)0);
 		}
diff --git a/libtiff/tif_win32.c b/libtiff/tif_win32.c
index d5db8b7f..955957b7 100644
--- a/libtiff/tif_win32.c
+++ b/libtiff/tif_win32.c
@@ -286,7 +286,7 @@ TIFFOpenExt(const char* name, const char* mode, TIFFOpenOptions* opts)
 	DWORD dwMode;
 	TIFF* tif;
 
-	m = _TIFFgetMode(mode, module);
+	m = _TIFFgetMode(opts, NULL, mode, module);
 
 	switch(m) {
 		case O_RDONLY:			dwMode = OPEN_EXISTING; break;
@@ -303,7 +303,7 @@ TIFFOpenExt(const char* name, const char* mode, TIFFOpenOptions* opts)
 		(m == O_RDONLY)?FILE_ATTRIBUTE_READONLY:FILE_ATTRIBUTE_NORMAL,
 		NULL);
 	if (fd == INVALID_HANDLE_VALUE) {
-		TIFFErrorExt(0, module, "%s: Cannot open", name);
+		_TIFFErrorEarly(opts, NULL, module, "%s: Cannot open", name);
 		return ((TIFF *)0);
 	}
 
@@ -333,7 +333,7 @@ TIFFOpenWExt(const wchar_t* name, const char* mode, TIFFOpenOptions* opts)
 	char *mbname;
 	TIFF *tif;
 
-	m = _TIFFgetMode(mode, module);
+	m = _TIFFgetMode(opts, NULL, mode, module);
 
 	switch(m) {
 		case O_RDONLY:			dwMode = OPEN_EXISTING; break;
@@ -350,7 +350,7 @@ TIFFOpenWExt(const wchar_t* name, const char* mode, TIFFOpenOptions* opts)
 		(m == O_RDONLY)?FILE_ATTRIBUTE_READONLY:FILE_ATTRIBUTE_NORMAL,
 		NULL);
 	if (fd == INVALID_HANDLE_VALUE) {
-		TIFFErrorExt(0, module, "%S: Cannot open", name);
+		_TIFFErrorEarly(opts, NULL, module, "%S: Cannot open", name);
 		return ((TIFF *)0);
 	}
 
@@ -359,7 +359,7 @@ TIFFOpenWExt(const wchar_t* name, const char* mode, TIFFOpenOptions* opts)
 	if (mbsize > 0) {
 		mbname = (char *)_TIFFmalloc(mbsize);
 		if (!mbname) {
-			TIFFErrorExt(0, module,
+			_TIFFErrorEarly(opts, NULL, module,
 			"Can't allocate space for filename conversion buffer");
 			return ((TIFF*)0);
 		}
diff --git a/libtiff/tiffiop.h b/libtiff/tiffiop.h
index 4d703edd..d52fc45f 100644
--- a/libtiff/tiffiop.h
+++ b/libtiff/tiffiop.h
@@ -331,7 +331,7 @@ typedef size_t TIFFIOSize_t;
 #if defined(__cplusplus)
 extern "C" {
 #endif
-extern int _TIFFgetMode(const char* mode, const char* module);
+extern int _TIFFgetMode(TIFFOpenOptions* opts, thandle_t clientdata, const char* mode, const char* module);
 extern int _TIFFNoRowEncode(TIFF* tif, uint8_t* pp, tmsize_t cc, uint16_t s);
 extern int _TIFFNoStripEncode(TIFF* tif, uint8_t* pp, tmsize_t cc, uint16_t s);
 extern int _TIFFNoTileEncode(TIFF*, uint8_t* pp, tmsize_t cc, uint16_t s);
@@ -367,6 +367,7 @@ extern TIFFErrorHandler _TIFFwarningHandler;
 extern TIFFErrorHandler _TIFFerrorHandler;
 extern TIFFErrorHandlerExt _TIFFwarningHandlerExt;
 extern TIFFErrorHandlerExt _TIFFerrorHandlerExt;
+void _TIFFErrorEarly(TIFFOpenOptions* opts, thandle_t clientdata, const char* module, const char* fmt, ...) TIFF_ATTRIBUTE((__format__ (__printf__,4,5)));
 
 extern uint32_t _TIFFMultiply32(TIFF*, uint32_t, uint32_t, const char*);
 extern uint64_t _TIFFMultiply64(TIFF*, uint64_t, uint64_t, const char*);