https://bugs.gentoo.org/921637
https://github.com/swig/swig/issues/2778
https://github.com/pygraphviz/pygraphviz/commit/6ecae22cc382407652f9d3b57e9da89a3a03043b

From 6ecae22cc382407652f9d3b57e9da89a3a03043b Mon Sep 17 00:00:00 2001
From: William S Fulton <wsf@fultondesigns.co.uk>
Date: Fri, 26 Jan 2024 00:52:44 +0000
Subject: [PATCH] Remove outdated pystrings.swg (#508)

Remove pystring.swg - a very ancient copy from SWIG which does not
compile when using swig-4.2.0. Instead SWIG's version of pystrings.swg
that is kept up to date in SWIG is used. SWIG_PYTHON_STRICT_BYTE_CHAR
needs to be defined in order to maintain the current behaviour of only
allowing Python 3 byte type instead of Python 3 string type as input.

As the output of swig is in graphviz_wrap.c and is committed to the
repo, the updates committed in this file (keeping the current swig-4.1.1
version). A later commit could update it to swig-4.2.0.

Fixes build problems on Fedora where SWIG_Python_str_AsChar no longer exists,
as reported at https://github.com/swig/swig/issues/2778.
---
 pygraphviz/graphviz.i      |   4 ++
 pygraphviz/graphviz_wrap.c | 106 ++++++++++++++++++++++++++++---------
 pystrings.swg              |  86 ------------------------------
 3 files changed, 84 insertions(+), 112 deletions(-)
 delete mode 100644 pystrings.swg

diff --git a/pygraphviz/graphviz.i b/pygraphviz/graphviz.i
index c63eb2d3..d7c49330 100644
--- a/pygraphviz/graphviz.i
+++ b/pygraphviz/graphviz.i
@@ -1,5 +1,9 @@
 %module graphviz
 
+%begin %{
+#define SWIG_PYTHON_STRICT_BYTE_CHAR
+%}
+
 %{
 #include "graphviz/cgraph.h"
 #include "graphviz/gvc.h"
diff --git a/pygraphviz/graphviz_wrap.c b/pygraphviz/graphviz_wrap.c
index 8f4d3a62..cb06da79 100644
--- a/pygraphviz/graphviz_wrap.c
+++ b/pygraphviz/graphviz_wrap.c
@@ -6,6 +6,9 @@
  * the SWIG interface file instead.
  * ----------------------------------------------------------------------------- */
 
+#define SWIG_PYTHON_STRICT_BYTE_CHAR
+
+
 
 #define SWIG_VERSION 0x040101
 #define SWIGPYTHON
@@ -3038,49 +3041,96 @@ SWIGINTERN int
 SWIG_AsCharPtrAndSize(PyObject *obj, char** cptr, size_t* psize, int *alloc)
 {
 #if PY_VERSION_HEX>=0x03000000
+#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR)
   if (PyBytes_Check(obj))
 #else
+  if (PyUnicode_Check(obj))
+#endif
+#else  
   if (PyString_Check(obj))
 #endif
   {
     char *cstr; Py_ssize_t len;
+    int ret = SWIG_OK;
 #if PY_VERSION_HEX>=0x03000000
-    PyBytes_AsStringAndSize(obj, &cstr, &len);
-    if(alloc) *alloc = SWIG_NEWOBJ;
+#if !defined(SWIG_PYTHON_STRICT_BYTE_CHAR)
+    if (!alloc && cptr) {
+        /* We can't allow converting without allocation, since the internal
+           representation of string in Python 3 is UCS-2/UCS-4 but we require
+           a UTF-8 representation.
+           TODO(bhy) More detailed explanation */
+        return SWIG_RuntimeError;
+    }
+    obj = PyUnicode_AsUTF8String(obj);
+    if (!obj)
+      return SWIG_TypeError;
+    if (alloc)
+      *alloc = SWIG_NEWOBJ;
+#endif
+    if (PyBytes_AsStringAndSize(obj, &cstr, &len) == -1)
+      return SWIG_TypeError;
 #else
-    PyString_AsStringAndSize(obj, &cstr, &len);
+    if (PyString_AsStringAndSize(obj, &cstr, &len) == -1)
+      return SWIG_TypeError;
 #endif
     if (cptr) {
       if (alloc) {
-	/*
-	   In python the user should not be able to modify the inner
-	   string representation. To warranty that, if you define
-	   SWIG_PYTHON_SAFE_CSTRINGS, a new/copy of the python string
-	   buffer is always returned.
-
-	   The default behavior is just to return the pointer value,
-	   so, be careful.
-	*/
-#if defined(SWIG_PYTHON_SAFE_CSTRINGS)
-	if (*alloc != SWIG_OLDOBJ)
-#else
-	if (*alloc == SWIG_NEWOBJ)
-#endif
-	  {
-	    *cptr = (char *)memcpy(malloc((len + 1)*sizeof(char)), cstr, sizeof(char)*(len + 1));
-	    *alloc = SWIG_NEWOBJ;
-	  }
-	else {
+	if (*alloc == SWIG_NEWOBJ) {
+	  *cptr = (char *)memcpy(malloc((len + 1)*sizeof(char)), cstr, sizeof(char)*(len + 1));
+	  *alloc = SWIG_NEWOBJ;
+	} else {
 	  *cptr = cstr;
 	  *alloc = SWIG_OLDOBJ;
 	}
       } else {
+#if PY_VERSION_HEX>=0x03000000
+#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR)
+	*cptr = PyBytes_AsString(obj);
+#else
+	assert(0); /* Should never reach here with Unicode strings in Python 3 */
+#endif
+#else
 	*cptr = SWIG_Python_str_AsChar(obj);
+        if (!*cptr)
+          ret = SWIG_TypeError;
+#endif
       }
     }
     if (psize) *psize = len + 1;
-    return SWIG_OK;
+#if PY_VERSION_HEX>=0x03000000 && !defined(SWIG_PYTHON_STRICT_BYTE_CHAR)
+    Py_XDECREF(obj);
+#endif
+    return ret;
   } else {
+#if defined(SWIG_PYTHON_2_UNICODE)
+#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR)
+#error "Cannot use both SWIG_PYTHON_2_UNICODE and SWIG_PYTHON_STRICT_BYTE_CHAR at once"
+#endif
+#if PY_VERSION_HEX<0x03000000
+    if (PyUnicode_Check(obj)) {
+      char *cstr; Py_ssize_t len;
+      if (!alloc && cptr) {
+        return SWIG_RuntimeError;
+      }
+      obj = PyUnicode_AsUTF8String(obj);
+      if (!obj)
+        return SWIG_TypeError;
+      if (PyString_AsStringAndSize(obj, &cstr, &len) != -1) {
+        if (cptr) {
+          if (alloc) *alloc = SWIG_NEWOBJ;
+          *cptr = (char *)memcpy(malloc((len + 1)*sizeof(char)), cstr, sizeof(char)*(len + 1));
+        }
+        if (psize) *psize = len + 1;
+
+        Py_XDECREF(obj);
+        return SWIG_OK;
+      } else {
+        Py_XDECREF(obj);
+      }
+    }
+#endif
+#endif
+
     swig_type_info* pchar_descriptor = SWIG_pchar_descriptor();
     if (pchar_descriptor) {
       void* vptr = 0;
@@ -3311,13 +3361,17 @@ SWIG_FromCharPtrAndSize(const char* carray, size_t size)
   if (carray) {
     if (size > INT_MAX) {
       swig_type_info* pchar_descriptor = SWIG_pchar_descriptor();
-      return pchar_descriptor ?
+      return pchar_descriptor ? 
 	SWIG_InternalNewPointerObj((char *)(carray), pchar_descriptor, 0) : SWIG_Py_Void();
     } else {
 #if PY_VERSION_HEX >= 0x03000000
-      return PyBytes_FromStringAndSize(carray, (int)(size));
+#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR)
+      return PyBytes_FromStringAndSize(carray, (Py_ssize_t)(size));
+#else
+      return PyUnicode_DecodeUTF8(carray, (Py_ssize_t)(size), "surrogateescape");
+#endif
 #else
-      return PyString_FromStringAndSize(carray, (int)(size));
+      return PyString_FromStringAndSize(carray, (Py_ssize_t)(size));
 #endif
     }
   } else {
diff --git a/pystrings.swg b/pystrings.swg
deleted file mode 100644
index 7988a353..00000000
--- a/pystrings.swg
+++ /dev/null
@@ -1,86 +0,0 @@
-/* Fixed fragments for work with bytes in Python 3. */
-
-%fragment("SWIG_AsCharPtrAndSize","header",fragment="SWIG_pchar_descriptor") {
-SWIGINTERN int
-SWIG_AsCharPtrAndSize(PyObject *obj, char** cptr, size_t* psize, int *alloc)
-{
-%#if PY_VERSION_HEX>=0x03000000
-  if (PyBytes_Check(obj))
-%#else
-  if (PyString_Check(obj))
-%#endif
-  {
-    char *cstr; Py_ssize_t len;
-%#if PY_VERSION_HEX>=0x03000000
-    PyBytes_AsStringAndSize(obj, &cstr, &len);
-    if(alloc) *alloc = SWIG_NEWOBJ;
-%#else
-    PyString_AsStringAndSize(obj, &cstr, &len);
-%#endif
-    if (cptr) {
-      if (alloc) {
-	/*
-	   In python the user should not be able to modify the inner
-	   string representation. To warranty that, if you define
-	   SWIG_PYTHON_SAFE_CSTRINGS, a new/copy of the python string
-	   buffer is always returned.
-
-	   The default behavior is just to return the pointer value,
-	   so, be careful.
-	*/
-%#if defined(SWIG_PYTHON_SAFE_CSTRINGS)
-	if (*alloc != SWIG_OLDOBJ)
-%#else
-	if (*alloc == SWIG_NEWOBJ)
-%#endif
-	  {
-	    *cptr = %new_copy_array(cstr, len + 1, char);
-	    *alloc = SWIG_NEWOBJ;
-	  }
-	else {
-	  *cptr = cstr;
-	  *alloc = SWIG_OLDOBJ;
-	}
-      } else {
-	*cptr = SWIG_Python_str_AsChar(obj);
-      }
-    }
-    if (psize) *psize = len + 1;
-    return SWIG_OK;
-  } else {
-    swig_type_info* pchar_descriptor = SWIG_pchar_descriptor();
-    if (pchar_descriptor) {
-      void* vptr = 0;
-      if (SWIG_ConvertPtr(obj, &vptr, pchar_descriptor, 0) == SWIG_OK) {
-	if (cptr) *cptr = (char *) vptr;
-	if (psize) *psize = vptr ? (strlen((char *)vptr) + 1) : 0;
-	if (alloc) *alloc = SWIG_OLDOBJ;
-	return SWIG_OK;
-      }
-    }
-  }
-  return SWIG_TypeError;
-}
-}
-
-%fragment("SWIG_FromCharPtrAndSize","header",fragment="SWIG_pchar_descriptor") {
-SWIGINTERNINLINE PyObject *
-SWIG_FromCharPtrAndSize(const char* carray, size_t size)
-{
-  if (carray) {
-    if (size > INT_MAX) {
-      swig_type_info* pchar_descriptor = SWIG_pchar_descriptor();
-      return pchar_descriptor ?
-	SWIG_InternalNewPointerObj(%const_cast(carray,char *), pchar_descriptor, 0) : SWIG_Py_Void();
-    } else {
-%#if PY_VERSION_HEX >= 0x03000000
-      return PyBytes_FromStringAndSize(carray, %numeric_cast(size,int));
-%#else
-      return PyString_FromStringAndSize(carray, %numeric_cast(size,int));
-%#endif
-    }
-  } else {
-    return SWIG_Py_Void();
-  }
-}
-}

