Index: /trunk/include/iprt/string.h
===================================================================
--- /trunk/include/iprt/string.h	(revision 30858)
+++ /trunk/include/iprt/string.h	(revision 30859)
@@ -380,4 +380,12 @@
 
 /**
+ * Purge all bad UTF-8 encoding in the string, replacing it with '?'.
+ *
+ * @returns The number of bad characters (0 if nothing was done).
+ * @param   psz         The string to purge.
+ */
+RTDECL(size_t) RTStrPurgeEncoding(char *psz);
+
+/**
  * Gets the number of code points the string is made up of, excluding
  * the terminator.
@@ -517,5 +525,6 @@
  * @returns iprt status code
  * @returns VERR_INVALID_UTF8_ENCODING if the encoding is invalid.
- * @param   ppsz        The string.
+ * @param   ppsz        The string cursor.
+ *                      This is advanced one character forward on failure.
  * @param   pCp         Where to store the unicode code point.
  *                      Stores RTUNICP_INVALID if the encoding is invalid.
@@ -583,4 +592,5 @@
  * @param   ppsz        Pointer to the string pointer. This will be updated to
  *                      point to the char following the current code point.
+ *                      This is advanced one character forward on failure.
  * @param   pCp         Where to store the code point.
  *                      RTUNICP_INVALID is stored here on failure.
Index: /trunk/src/VBox/Runtime/common/string/utf-8.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/string/utf-8.cpp	(revision 30858)
+++ /trunk/src/VBox/Runtime/common/string/utf-8.cpp	(revision 30859)
@@ -326,4 +326,27 @@
 }
 RT_EXPORT_SYMBOL(RTStrIsValidEncoding);
+
+
+RTDECL(size_t) RTStrPurgeEncoding(char *psz)
+{
+    size_t cErrors = 0;
+    for (;;)
+    {
+        RTUNICP Cp;
+        int rc = RTStrGetCpEx((const char **)&psz, &Cp);
+        if (RT_SUCCESS(rc))
+        {
+            if (!Cp)
+                break;
+        }
+        else
+        {
+            psz[-1] = '?';
+            cErrors++;
+        }
+    }
+    return cErrors;
+}
+RT_EXPORT_SYMBOL(RTStrPurgeEncoding);
 
 
Index: /trunk/src/VBox/Runtime/testcase/tstUtf8.cpp
===================================================================
--- /trunk/src/VBox/Runtime/testcase/tstUtf8.cpp	(revision 30858)
+++ /trunk/src/VBox/Runtime/testcase/tstUtf8.cpp	(revision 30859)
@@ -788,4 +788,61 @@
 }
 
+
+
+/**
+ * Check case insensitivity.
+ */
+void TstRTStrPurgeEncoding(RTTEST hTest)
+{
+    RTTestSub(hTest, "RTStrPurgeEncoding");
+
+    /*
+     * Test some good strings.
+     */
+    char sz1[] = "1234567890wertyuiopsdfghjklzxcvbnm";
+    char sz1Copy[sizeof(sz1)];
+    memcpy(sz1Copy, sz1, sizeof(sz1));
+
+    RTTESTI_CHECK_RETV(RTStrPurgeEncoding(sz1) == 0);
+    RTTESTI_CHECK_RETV(!memcmp(sz1, sz1Copy, sizeof(sz1)));
+
+    char *pszAll = RTStrDup(g_szAll);
+    if (pszAll)
+    {
+        RTTESTI_CHECK(RTStrPurgeEncoding(pszAll) == 0);
+        RTTESTI_CHECK(!memcmp(pszAll, g_szAll, sizeof(g_szAll)));
+        RTStrFree(pszAll);
+    }
+
+    /*
+     * Test some bad stuff.
+     */
+    struct
+    {
+        size_t          cErrors;
+        unsigned char   szIn[5];
+        const char     *pszExpect;
+    } aTests[] =
+    {
+        { 0, {  '1',  '2',  '3',  '4', '\0' }, "1234" },
+        { 1, { 0x80,  '2',  '3',  '4', '\0' }, "?234" },
+        { 1, {  '1', 0x80,  '3',  '4', '\0' }, "1?34" },
+        { 1, {  '1',  '2', 0x80,  '4', '\0' }, "12?4" },
+        { 1, {  '1',  '2',  '3', 0x80, '\0' }, "123?" },
+        { 2, { 0x80, 0x81,  '3',  '4', '\0' }, "??34" },
+        { 2, {  '1', 0x80, 0x81,  '4', '\0' }, "1??4" },
+        { 2, {  '1',  '2', 0x80, 0x81, '\0' }, "12??" },
+    };
+    for (size_t i = 0; i < RT_ELEMENTS(aTests); i++)
+    {
+        size_t cErrors = RTStrPurgeEncoding((char *)aTests[i].szIn);
+        if (cErrors != aTests[i].cErrors)
+            RTTestFailed(hTest, "#%u: cErrors=%u expected %u\n", i, cErrors, aTests[i].cErrors);
+        else if (strcmp((char *)aTests[i].szIn, aTests[i].pszExpect))
+            RTTestFailed(hTest, "#%u: %.5Rhxs expected %.5Rhxs (%s)\n", i, aTests[i].szIn, aTests[i].pszExpect, aTests[i].pszExpect);
+    }
+
+    RTTestSubDone(hTest);
+}
 
 
@@ -1238,4 +1295,5 @@
     test3(hTest);
     TstRTStrXCmp(hTest);
+    TstRTStrPurgeEncoding(hTest);
     testStrEnd(hTest);
     testStrStr(hTest);
