Merge branch 'master' into dev
diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h
index cd85d97..8c6c125 100644
--- a/inc/qcbor/qcbor_common.h
+++ b/inc/qcbor/qcbor_common.h
@@ -534,8 +534,15 @@
    /* Trying to encode something that is discouraged (e.g., 65-bit
     * negative integer) without allowing it by calling
     * QCBOREncode_Allow() */
-   QCBOR_ERR_NOT_ALLOWED = 80
+   QCBOR_ERR_NOT_ALLOWED = 80,
 
+   /** A range of error codes that can be made use of by the
+    * caller. QCBOR internally does nothing with these except notice
+    * that they are not QCBOR_SUCCESS. See QCBORDecode_SetError(). */
+   QCBOR_ERR_FIRST_USER_DEFINED = 128,
+
+   /** See \ref QCBOR_ERR_FIRST_USER_DEFINED */
+   QCBOR_ERR_LAST_USER_DEFINED = 255
 
    /* This is stored in uint8_t; never add values > 255 */
 } QCBORError;
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index f40200a..69dbbc1 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -121,8 +121,11 @@
  * call QCBORDecode_GetError() to know the earlier items were
  * successfully decoded before examining their value or type.
  *
- * The internal decode error state is reset only by re initializing the
- * decoder or calling QCBORDecode_GetErrorAndReset().
+ * The internal decode error state can be reset by reinitializing the
+ * decoder or calling QCBORDecode_GetErrorAndReset(). Code calling
+ * QCBOR may take advantage of the internal error state to halt
+ * futher decoding and propagate errors it detects using
+ * QCBORDecode_SetError().
  *
  * It is only useful to reset the error state by calling
  * QCBORDecode_GetErrorAndReset() on recoverable errors. Examples of
@@ -900,9 +903,10 @@
  *
  * See [Decode Error Overview](#Decode-Errors-Overview).
  *
- * If a decoding error occurs, \c uDataType and \c uLabelType will be set
- * to @ref QCBOR_TYPE_NONE. If there is no need to know the specific
- * error, it is sufficient to check for @ref QCBOR_TYPE_NONE.
+ * If a decoding error occurs or previously occured, \c uDataType and
+ * \c uLabelType will be set to @ref QCBOR_TYPE_NONE. If there is no
+ * need to know the specific error, it is sufficient to check for @ref
+ * QCBOR_TYPE_NONE.
  *
  * Errors fall in several categories:
  *
@@ -1212,7 +1216,7 @@
 /**
  * @brief Whether an error indicates non-well-formed CBOR.
  *
- * @param[in] uErr    The decoder context.
+ * @param[in] uErr    The QCBOR error code.
  * @return @c true if the error code indicates non-well-formed CBOR.
  */
 static bool
@@ -1222,7 +1226,7 @@
 /**
  * @brief Whether a decoding error is recoverable.
  *
- * @param[in] uErr    The decoder context.
+ * @param[in] uErr    The QCBOR error code.
  * @return @c true if the error code indicates and uncrecoverable error.
  *
  * When an error is unrecoverable, no further decoding of the input is
@@ -1242,6 +1246,30 @@
 QCBORDecode_IsUnrecoverableError(QCBORError uErr);
 
 
+/**
+ * @brief Manually set error condition, or set user-defined error.
+ *
+ * @param[in] pCtx    The decoder context.
+ * @param[in] uError  The error code to set.
+ *
+ * Once set, none of the QCBORDecode methods will do anything and the
+ * error code set will stay until cleared with
+ * QCBORDecode_GetAndResetError().  A user-defined error can be set
+ * deep in some decoding layers to short-circuit further decoding
+ * and propagate up.
+ *
+ * When the error condition is set, QCBORDecode_VGetNext() will always
+ * return an item with data and label type as \ref QCBOR_TYPE_NONE.
+ *
+ * The main intent of this is to set a user-defined error code in the
+ * range of \ref QCBOR_ERR_FIRST_USER_DEFINED to
+ * \ref QCBOR_ERR_LAST_USER_DEFINED, but it is OK to set QCBOR-defined
+ * error codes too.
+ */
+static void
+QCBORDecode_SetError(QCBORDecodeContext *pCtx, QCBORError uError);
+
+
 
 
 /**
@@ -1530,6 +1558,14 @@
    }
 }
 
+
+static inline void
+QCBORDecode_SetError(QCBORDecodeContext *pMe, QCBORError uError)
+{
+   pMe->uLastError = (uint8_t)uError;
+}
+
+
 /* A few cross checks on size constants and special value lengths */
 #if  QCBOR_MAP_OFFSET_CACHE_INVALID < QCBOR_MAX_DECODE_INPUT_SIZE
 #error QCBOR_MAP_OFFSET_CACHE_INVALID is too large
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index 19054a9..bb8d4d4 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -2658,6 +2658,8 @@
 QCBORDecode_VPeekNext(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem)
 {
    if(pMe->uLastError != QCBOR_SUCCESS) {
+      pDecodedItem->uDataType  = QCBOR_TYPE_NONE;
+      pDecodedItem->uLabelType = QCBOR_TYPE_NONE;
       return;
    }
 
@@ -2672,6 +2674,8 @@
 QCBORDecode_VGetNext(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem)
 {
    if(pMe->uLastError != QCBOR_SUCCESS) {
+      pDecodedItem->uDataType  = QCBOR_TYPE_NONE;
+      pDecodedItem->uLabelType = QCBOR_TYPE_NONE;
       return;
    }
 
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 76e7c3f..dc5d070 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -8830,3 +8830,85 @@
 
    return 0;
 }
+
+
+int32_t
+ErrorHandlingTests(void)
+{
+   QCBORDecodeContext DCtx;
+   QCBORItem          Item;
+   QCBORError         uError;
+   int64_t            integer;
+
+   /* Test QCBORDecode_SetError() */
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapEncoded),
+                    QCBOR_DECODE_MODE_NORMAL);
+
+   QCBORDecode_SetError(&DCtx, QCBOR_ERR_FIRST_USER_DEFINED);
+
+   QCBORDecode_VGetNext(&DCtx, &Item);
+
+   uError = QCBORDecode_GetError(&DCtx);
+
+   if(uError != QCBOR_ERR_FIRST_USER_DEFINED) {
+      return -1;
+   }
+
+   if(Item.uLabelType != QCBOR_TYPE_NONE ||
+      Item.uDataType != QCBOR_TYPE_NONE) {
+      return -2;
+   }
+
+
+   /* Test data type returned from previous error */
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapEncoded),
+                    QCBOR_DECODE_MODE_NORMAL);
+   QCBORDecode_GetInt64(&DCtx, &integer);
+   uError = QCBORDecode_GetError(&DCtx);
+   if(uError != QCBOR_ERR_UNEXPECTED_TYPE) {
+      return -3;
+   }
+
+   QCBORDecode_VGetNext(&DCtx, &Item);
+   if(Item.uLabelType != QCBOR_TYPE_NONE ||
+      Item.uDataType != QCBOR_TYPE_NONE) {
+      return -2;
+   }
+   uError = QCBORDecode_GetError(&DCtx);
+   if(uError != QCBOR_ERR_UNEXPECTED_TYPE) {
+      return -3;
+   }
+
+
+   /* Test error classification functions */
+
+   if(!QCBORDecode_IsUnrecoverableError(QCBOR_ERR_INDEFINITE_STRING_CHUNK)) {
+      return -10;
+   }
+   if(QCBORDecode_IsUnrecoverableError(QCBOR_SUCCESS)) {
+      return -11;
+   }
+   if(!QCBORDecode_IsUnrecoverableError(QCBOR_ERR_INDEFINITE_STRING_CHUNK)) {
+      return -12;
+   }
+   if(QCBORDecode_IsUnrecoverableError(QCBOR_ERR_DUPLICATE_LABEL)) {
+      return -13;
+   }
+
+   if(!QCBORDecode_IsNotWellFormedError(QCBOR_ERR_BAD_TYPE_7)) {
+      return -20;
+   }
+   if(!QCBORDecode_IsNotWellFormedError(QCBOR_ERR_BAD_BREAK)) {
+      return -21;
+   }
+   if(QCBORDecode_IsNotWellFormedError(QCBOR_SUCCESS)) {
+      return -22;
+   }
+   if(QCBORDecode_IsNotWellFormedError(QCBOR_ERR_ARRAY_DECODE_TOO_LONG)) {
+      return -23;
+   }
+
+   return 0;
+}
diff --git a/test/qcbor_decode_tests.h b/test/qcbor_decode_tests.h
index 11fdc94..0cedf43 100644
--- a/test/qcbor_decode_tests.h
+++ b/test/qcbor_decode_tests.h
@@ -318,4 +318,9 @@
 */
 int32_t CBORTestIssue134(void);
 
+
+
+int32_t ErrorHandlingTests(void);
+
+
 #endif /* defined(__QCBOR__qcbort_decode_tests__) */
diff --git a/test/run_tests.c b/test/run_tests.c
index bfc9dd9..c61fcef 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -67,6 +67,7 @@
 
 
 static test_entry s_tests[] = {
+   TEST_ENTRY(ErrorHandlingTests),
    TEST_ENTRY(OpenCloseBytesTest),
    TEST_ENTRY(EnterBstrTest),
    TEST_ENTRY(IntegerConvertTest),