Index: /pjproject/trunk/pjlib/include/pj/errno.h
===================================================================
--- /pjproject/trunk/pjlib/include/pj/errno.h	(revision 127)
+++ /pjproject/trunk/pjlib/include/pj/errno.h	(revision 128)
@@ -107,4 +107,27 @@
 			       char *buf, pj_size_t bufsize);
 
+/**
+ * Register strerror message handler for the specified error space.
+ * Application can register its own handler to supply the error message
+ * for the specified error code range. This handler will be called
+ * by #pj_strerror().
+ *
+ * @param start_code	The starting error code where the handler should
+ *			be called to retrieve the error message.
+ * @param err_space	The size of error space. The error code range then
+ *			will fall in start_code to start_code+err_space-1
+ *			range.
+ * @param f		The handler to be called when #pj_strerror() is
+ *			supplied with error code that falls into this range.
+ *
+ * @return		PJ_SUCCESS or the specified error code. The 
+ *			registration may fail when the error space has been
+ *			occupied by other handler, or when there are too many
+ *			handlers registered to PJLIB.
+ */
+PJ_DECL(pj_status_t) pj_register_strerror(pj_status_t start_code,
+					  pj_status_t err_space,
+					  pj_str_t (*f)(pj_status_t,char*,
+							pj_size_t));
 
 /**
Index: /pjproject/trunk/pjlib/src/pj/errno.c
===================================================================
--- /pjproject/trunk/pjlib/src/pj/errno.c	(revision 127)
+++ /pjproject/trunk/pjlib/src/pj/errno.c	(revision 128)
@@ -19,4 +19,5 @@
 #include <pj/errno.h>
 #include <pj/string.h>
+#include <pj/assert.h>
 #include <pj/compat/sprintf.h>
 
@@ -26,4 +27,16 @@
 extern int platform_strerror( pj_os_err_type code, 
                               char *buf, pj_size_t bufsize );
+
+#define PJLIB_MAX_ERR_MSG_HANDLER   8
+
+/* Error message handler. */
+static unsigned err_msg_hnd_cnt;
+static struct err_msg_hnd
+{
+    pj_status_t	    begin;
+    pj_status_t	    end;
+    pj_str_t	  (*strerror)(pj_status_t, char*, pj_size_t);
+
+} err_msg_hnd[PJLIB_MAX_ERR_MSG_HANDLER];
 
 /* PJLIB's own error codes/messages */
@@ -77,4 +90,43 @@
 }
 
+#define IN_RANGE(val,start,end)	    ((val)>=(start) && (val)<(end))
+
+/* Register strerror handle. */
+PJ_DECL(pj_status_t) pj_register_strerror(pj_status_t start,
+					  pj_status_t space,
+					  pj_str_t (*f)(pj_status_t,char*,
+							pj_size_t))
+{
+    unsigned i;
+
+    /* Check arguments. */
+    PJ_ASSERT_RETURN(start && space && f, PJ_EINVAL);
+
+    /* Check if there aren't too many handlers registered. */
+    PJ_ASSERT_RETURN(err_msg_hnd_cnt < PJ_ARRAY_SIZE(err_msg_hnd),
+		     PJ_ETOOMANY);
+
+    /* Start error must be greater than PJ_ERRNO_START_USER */
+    PJ_ASSERT_RETURN(start >= PJ_ERRNO_START_USER, PJ_EEXISTS);
+
+    /* Check that no existing handler has covered the specified range. */
+    for (i=0; i<err_msg_hnd_cnt; ++i) {
+	if (IN_RANGE(start, err_msg_hnd[i].begin, err_msg_hnd[i].end) ||
+	    IN_RANGE(start+space-1, err_msg_hnd[i].begin, err_msg_hnd[i].end))
+	{
+	    return PJ_EEXISTS;
+	}
+    }
+
+    /* Register the handler. */
+    err_msg_hnd[err_msg_hnd_cnt].begin = start;
+    err_msg_hnd[err_msg_hnd_cnt].end = start + space;
+    err_msg_hnd[err_msg_hnd_cnt].strerror = f;
+
+    ++err_msg_hnd_cnt;
+
+    return PJ_SUCCESS;
+}
+
 /*
  * pj_strerror()
@@ -85,4 +137,6 @@
     int len = -1;
     pj_str_t errstr;
+
+    pj_assert(buf && bufsize);
 
     if (statcode < PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE) {
@@ -95,10 +149,13 @@
         len = platform_strerror(PJ_STATUS_TO_OS(statcode), buf, bufsize);
 
-    } else if (statcode < PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE) {
-        len = pj_snprintf( buf, bufsize, "User error %d", statcode);
+    } else {
+	unsigned i;
 
-    } else {
-        len = pj_snprintf( buf, bufsize, "Invalid error %d", statcode);
-
+	/* Find user handler to get the error message. */
+	for (i=0; i<err_msg_hnd_cnt; ++i) {
+	    if (IN_RANGE(statcode, err_msg_hnd[i].begin, err_msg_hnd[i].end)) {
+		return (*err_msg_hnd[i].strerror)(statcode, buf, bufsize);
+	    }
+	}
     }
 
