Bring string descriptions of errors up to date (#235)

* Bring string descriptions of errors up to date

* tidiness

---------

Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/QCBOR.xcodeproj/project.pbxproj b/QCBOR.xcodeproj/project.pbxproj
index 6e69fbd..e43bf04 100644
--- a/QCBOR.xcodeproj/project.pbxproj
+++ b/QCBOR.xcodeproj/project.pbxproj
@@ -173,7 +173,7 @@
 		E776E094214AE09700E67947 /* UsefulBuf.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = UsefulBuf.h; path = inc/UsefulBuf.h; sourceTree = "<group>"; tabWidth = 3; };
 		E776E096214AE0C700E67947 /* cmd_line_main.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.c; path = cmd_line_main.c; sourceTree = "<group>"; tabWidth = 3; };
 		E776E161214EE19C00E67947 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
-		E7864765252CE63100A0C11B /* qcbor_err_to_str.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = qcbor_err_to_str.c; path = src/qcbor_err_to_str.c; sourceTree = "<group>"; };
+		E7864765252CE63100A0C11B /* qcbor_err_to_str.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.c; name = qcbor_err_to_str.c; path = src/qcbor_err_to_str.c; sourceTree = "<group>"; tabWidth = 3; };
 		E78C91DE240C90C100F4CECE /* qcbor_decode.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = qcbor_decode.h; path = inc/qcbor/qcbor_decode.h; sourceTree = "<group>"; tabWidth = 3; };
 		E78C91DF240C90C100F4CECE /* qcbor_common.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = qcbor_common.h; path = inc/qcbor/qcbor_common.h; sourceTree = "<group>"; tabWidth = 3; };
 		E78C91E0240C90C100F4CECE /* qcbor_private.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = qcbor_private.h; path = inc/qcbor/qcbor_private.h; sourceTree = "<group>"; tabWidth = 3; };
diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h
index 752d149..20643ab 100644
--- a/inc/qcbor/qcbor_common.h
+++ b/inc/qcbor/qcbor_common.h
@@ -543,12 +543,17 @@
 /**
  * @brief Get string describing an error code.
  *
- * @param[in] err   The error code.
+ * @param[in] uErr   The error code.
  *
  * @return  NULL-terminated string describing error or "Unidentified
  *          error" if the error is not known.
+ *
+ * This is not thread-safe because it uses a static buffer
+ * for formatting, but this is only a diagnostic and the only
+ * consequence is the wrong description.
  */
-const char *qcbor_err_to_str(QCBORError err);
+const char *
+qcbor_err_to_str(QCBORError uErr);
 
 
 
diff --git a/src/qcbor_err_to_str.c b/src/qcbor_err_to_str.c
index 4879f91..87c2b28 100644
--- a/src/qcbor_err_to_str.c
+++ b/src/qcbor_err_to_str.c
@@ -1,69 +1,92 @@
-/*==============================================================================
- err_to_str.c -- strings names for errors
-
- Copyright (c) 2020, Patrick Uiterwijk. All rights reserved.
- Copyright (c) 2020, Laurence Lundblade.
- Copyright (c) 2021, Arm Limited. All rights reserved.
-
- SPDX-License-Identifier: BSD-3-Clause
-
- See BSD-3-Clause license in README.md
-
- Created on 3/21/20
- =============================================================================*/
+/* ==========================================================================
+ * err_to_str.c -- strings names for errors
+ *
+ * Copyright (c) 2020, Patrick Uiterwijk. All rights reserved.
+ * Copyright (c) 2020,2024, Laurence Lundblade.
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ *
+ * Created on 3/21/20
+ * ========================================================================== */
 
 #include "qcbor/qcbor_common.h"
+#include <string.h>
 
-#define _ERR_TO_STR(errpart) case QCBOR_##errpart: return "QCBOR_" #errpart;
+#define ERR_TO_STR_CASE(errpart) case errpart: return #errpart;
 
-const char *qcbor_err_to_str(QCBORError err) {
-    switch (err) {
-    _ERR_TO_STR(SUCCESS)
-    _ERR_TO_STR(ERR_BUFFER_TOO_SMALL)
-    _ERR_TO_STR(ERR_ENCODE_UNSUPPORTED)
-    _ERR_TO_STR(ERR_BUFFER_TOO_LARGE)
-    _ERR_TO_STR(ERR_ARRAY_NESTING_TOO_DEEP)
-    _ERR_TO_STR(ERR_CLOSE_MISMATCH)
-    _ERR_TO_STR(ERR_ARRAY_TOO_LONG)
-    _ERR_TO_STR(ERR_TOO_MANY_CLOSES)
-    _ERR_TO_STR(ERR_ARRAY_OR_MAP_STILL_OPEN)
-    _ERR_TO_STR(ERR_BAD_TYPE_7)
-    _ERR_TO_STR(ERR_EXTRA_BYTES)
-    _ERR_TO_STR(ERR_UNSUPPORTED)
-    _ERR_TO_STR(ERR_ARRAY_OR_MAP_UNCONSUMED)
-    _ERR_TO_STR(ERR_BAD_INT)
-    _ERR_TO_STR(ERR_INDEFINITE_STRING_CHUNK)
-    _ERR_TO_STR(ERR_HIT_END)
-    _ERR_TO_STR(ERR_BAD_BREAK)
-    _ERR_TO_STR(ERR_INPUT_TOO_LARGE)
-    _ERR_TO_STR(ERR_ARRAY_DECODE_NESTING_TOO_DEEP)
-    _ERR_TO_STR(ERR_ARRAY_DECODE_TOO_LONG)
-    _ERR_TO_STR(ERR_STRING_TOO_LONG)
-    _ERR_TO_STR(ERR_BAD_EXP_AND_MANTISSA)
-    _ERR_TO_STR(ERR_NO_STRING_ALLOCATOR)
-    _ERR_TO_STR(ERR_STRING_ALLOCATE)
-    _ERR_TO_STR(ERR_TOO_MANY_TAGS)
-    _ERR_TO_STR(ERR_MAP_LABEL_TYPE)
-    _ERR_TO_STR(ERR_UNEXPECTED_TYPE)
-    _ERR_TO_STR(ERR_BAD_OPT_TAG)
-    _ERR_TO_STR(ERR_DUPLICATE_LABEL)
-    _ERR_TO_STR(ERR_MEM_POOL_SIZE)
-    _ERR_TO_STR(ERR_INT_OVERFLOW)
-    _ERR_TO_STR(ERR_DATE_OVERFLOW)
-    _ERR_TO_STR(ERR_EXIT_MISMATCH)
-    _ERR_TO_STR(ERR_NO_MORE_ITEMS)
-    _ERR_TO_STR(ERR_LABEL_NOT_FOUND)
-    _ERR_TO_STR(ERR_NUMBER_SIGN_CONVERSION)
-    _ERR_TO_STR(ERR_CONVERSION_UNDER_OVER_FLOW)
-    _ERR_TO_STR(ERR_MAP_NOT_ENTERED)
-    _ERR_TO_STR(ERR_CALLBACK_FAIL)
-    _ERR_TO_STR(ERR_FLOAT_DATE_DISABLED)
-    _ERR_TO_STR(ERR_HALF_PRECISION_DISABLED)
-    _ERR_TO_STR(ERR_HW_FLOAT_DISABLED)
-    _ERR_TO_STR(ERR_FLOAT_EXCEPTION)
-    _ERR_TO_STR(ERR_ALL_FLOAT_DISABLED)
 
-    default:
-        return "Unidentified error";
-    }
+const char *
+qcbor_err_to_str(const QCBORError uErr) {
+   switch (uErr) {
+   ERR_TO_STR_CASE(QCBOR_SUCCESS)
+   ERR_TO_STR_CASE(QCBOR_ERR_BUFFER_TOO_SMALL)
+   ERR_TO_STR_CASE(QCBOR_ERR_ENCODE_UNSUPPORTED)
+   ERR_TO_STR_CASE(QCBOR_ERR_BUFFER_TOO_LARGE)
+   ERR_TO_STR_CASE(QCBOR_ERR_ARRAY_NESTING_TOO_DEEP)
+   ERR_TO_STR_CASE(QCBOR_ERR_CLOSE_MISMATCH)
+   ERR_TO_STR_CASE(QCBOR_ERR_ARRAY_TOO_LONG)
+   ERR_TO_STR_CASE(QCBOR_ERR_TOO_MANY_CLOSES)
+   ERR_TO_STR_CASE(QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN)
+   ERR_TO_STR_CASE(QCBOR_ERR_OPEN_BYTE_STRING)
+   ERR_TO_STR_CASE(QCBOR_ERR_CANNOT_CANCEL)
+   ERR_TO_STR_CASE(QCBOR_ERR_BAD_TYPE_7)
+   ERR_TO_STR_CASE(QCBOR_ERR_EXTRA_BYTES)
+   ERR_TO_STR_CASE(QCBOR_ERR_UNSUPPORTED)
+   ERR_TO_STR_CASE(QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED)
+   ERR_TO_STR_CASE(QCBOR_ERR_BAD_INT)
+   ERR_TO_STR_CASE(QCBOR_ERR_INDEFINITE_STRING_CHUNK)
+   ERR_TO_STR_CASE(QCBOR_ERR_HIT_END)
+   ERR_TO_STR_CASE(QCBOR_ERR_BAD_BREAK)
+   ERR_TO_STR_CASE(QCBOR_ERR_INPUT_TOO_LARGE)
+   ERR_TO_STR_CASE(QCBOR_ERR_ARRAY_DECODE_NESTING_TOO_DEEP)
+   ERR_TO_STR_CASE(QCBOR_ERR_ARRAY_DECODE_TOO_LONG)
+   ERR_TO_STR_CASE(QCBOR_ERR_STRING_TOO_LONG)
+   ERR_TO_STR_CASE(QCBOR_ERR_BAD_EXP_AND_MANTISSA)
+   ERR_TO_STR_CASE(QCBOR_ERR_NO_STRING_ALLOCATOR)
+   ERR_TO_STR_CASE(QCBOR_ERR_STRING_ALLOCATE)
+   ERR_TO_STR_CASE(QCBOR_ERR_MAP_LABEL_TYPE)
+   ERR_TO_STR_CASE(QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT)
+   ERR_TO_STR_CASE(QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED)
+   ERR_TO_STR_CASE(QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED)
+   ERR_TO_STR_CASE(QCBOR_ERR_TAGS_DISABLED)
+   ERR_TO_STR_CASE(QCBOR_ERR_TOO_MANY_TAGS)
+   ERR_TO_STR_CASE(QCBOR_ERR_UNEXPECTED_TYPE)
+   ERR_TO_STR_CASE(QCBOR_ERR_DUPLICATE_LABEL)
+   ERR_TO_STR_CASE(QCBOR_ERR_MEM_POOL_SIZE)
+   ERR_TO_STR_CASE(QCBOR_ERR_INT_OVERFLOW)
+   ERR_TO_STR_CASE(QCBOR_ERR_DATE_OVERFLOW)
+   ERR_TO_STR_CASE(QCBOR_ERR_EXIT_MISMATCH)
+   ERR_TO_STR_CASE(QCBOR_ERR_NO_MORE_ITEMS)
+   ERR_TO_STR_CASE(QCBOR_ERR_LABEL_NOT_FOUND)
+   ERR_TO_STR_CASE(QCBOR_ERR_NUMBER_SIGN_CONVERSION)
+   ERR_TO_STR_CASE(QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW)
+   ERR_TO_STR_CASE(QCBOR_ERR_MAP_NOT_ENTERED)
+   ERR_TO_STR_CASE(QCBOR_ERR_CALLBACK_FAIL)
+   ERR_TO_STR_CASE(QCBOR_ERR_FLOAT_DATE_DISABLED)
+   ERR_TO_STR_CASE(QCBOR_ERR_HALF_PRECISION_DISABLED)
+   ERR_TO_STR_CASE(QCBOR_ERR_HW_FLOAT_DISABLED)
+   ERR_TO_STR_CASE(QCBOR_ERR_FLOAT_EXCEPTION)
+   ERR_TO_STR_CASE(QCBOR_ERR_ALL_FLOAT_DISABLED)
+   ERR_TO_STR_CASE(QCBOR_ERR_RECOVERABLE_BAD_TAG_CONTENT)
+   ERR_TO_STR_CASE(QCBOR_ERR_CANNOT_ENTER_ALLOCATED_STRING)
+
+   default:
+      if(uErr >= QCBOR_ERR_FIRST_USER_DEFINED && uErr <= QCBOR_ERR_LAST_USER_DEFINED) {
+         /* Static buffer is not thread safe, but this is only a diagnostic */
+         static char buf[20];
+         strcpy(buf, "USER_DEFINED_");
+         size_t uEndOffset = strlen(buf);
+         buf[uEndOffset]   = (char)(uErr/100 + '0');
+         buf[uEndOffset+1] = (char)(((uErr/10) % 10) + '0');
+         buf[uEndOffset+2] = (char)((uErr % 10 )+ '0');
+         buf[uEndOffset+3] = '\0';
+         return buf;
+
+      } else {
+         return "Unidentified QCBOR error";
+      }
+   }
 }
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index c09b4a9..eeed238 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -9900,6 +9900,41 @@
       return -23;
    }
 
+   /* Test error strings */
+   const char *szErrString;
+
+   szErrString = qcbor_err_to_str(QCBOR_ERR_ARRAY_DECODE_TOO_LONG);
+   if(szErrString == NULL) {
+      return -100;
+   }
+   if(strcmp(szErrString, "QCBOR_ERR_ARRAY_DECODE_TOO_LONG")) {
+      return -101;
+   }
+
+   szErrString = qcbor_err_to_str(QCBOR_SUCCESS);
+   if(szErrString == NULL) {
+      return -102;
+   }
+   if(strcmp(szErrString, "QCBOR_SUCCESS")) {
+      return -103;
+   }
+
+   szErrString = qcbor_err_to_str(100);
+   if(szErrString == NULL) {
+      return -104;
+   }
+   if(strcmp(szErrString, "Unidentified QCBOR error")) {
+      return -105;
+   }
+
+   szErrString = qcbor_err_to_str(200);
+   if(szErrString == NULL) {
+      return -106;
+   }
+   if(strcmp(szErrString, "USER_DEFINED_200")) {
+      return -107;
+   }
+
    return 0;
 }