Index: pjproject/trunk/pjlib/include/pj/errno.h
===================================================================
--- pjproject/trunk/pjlib/include/pj/errno.h	(revision 2991)
+++ pjproject/trunk/pjlib/include/pj/errno.h	(revision 2992)
@@ -23,19 +23,22 @@
 /**
  * @file errno.h
- * @brief PJLIB Error Codes
+ * @brief PJLIB Error Subsystem
  */
 #include <pj/types.h>
 #include <pj/compat/errno.h>
+#include <stdarg.h>
 
 PJ_BEGIN_DECL
 
 /**
- * @defgroup pj_errno Error Codes
+ * @defgroup pj_errno Error Subsystem
  * @{
  *
- * In PJLIB, error/status codes from operating system are translated
- * into PJLIB error namespace, and stored in @a pj_status_t. All functions
- * that work with @a pj_status_t expect to get PJLIB error code instead
- * of native codes.
+ * The PJLIB Error Subsystem is a framework to unify all error codes
+ * produced by all components into a single error space, and provide
+ * uniform set of APIs to access them. With this framework, any error
+ * codes are encoded as pj_status_t value. The framework is extensible,
+ * application may register new error spaces to be recognized by
+ * the framework.
  *
  * @section pj_errno_retval Return Values
@@ -48,18 +51,31 @@
  * before returning the error to caller.
  *
- * @section pj_errno_errmsg Error Message
- *
- * To get the error message corresponding to a particular code, use function
- * #pj_strerror(). This function expects error code in PJLIB error namespace,
- * not the native error code. Application can pass the value from the 
- * following sources to this function:
- *  - #pj_get_os_error()
- *  - #pj_get_netos_error()
- *  - any return value from function returning @a pj_status_t.
- *
- * Application MUST NOT pass native error code (such as error code from
+ * @section err_services Retrieving and Displaying Error Messages
+ *
+ * The framework provides the following APIs to retrieve and/or display
+ * error messages:
+ *
+ *   - #pj_strerror(): this is the base API to retrieve error string
+ *      description for the specified pj_status_t error code.
+ *
+ *   - #PJ_PERROR() macro: use this macro similar to PJ_LOG to format
+ *      an error message and display them to the log
+ *
+ *   - #pj_perror(): this function is similar to PJ_PERROR() but unlike
+ *      #PJ_PERROR(), this function will always be included in the
+ *      link process. Due to this reason, prefer to use #PJ_PERROR()
+ *      if the application is concerned about the executable size.
+ *
+ * Application MUST NOT pass native error codes (such as error code from
  * functions like GetLastError() or errno) to PJLIB functions expecting
  * @a pj_status_t.
  *
+ * @section err_extending Extending the Error Space
+ *
+ * Application may register new error space to be recognized by the
+ * framework by using #pj_register_strerror(). Use the range started
+ * from PJ_ERRNO_START_USER to avoid conflict with existing error
+ * spaces.
+ *
  */
 
@@ -68,4 +84,12 @@
  */
 #define PJ_ERR_MSG_SIZE  80
+
+/**
+ * Buffer for title string of #PJ_PERROR().
+ */
+#ifndef PJ_PERROR_TITLE_BUF_SIZE
+#   define PJ_PERROR_TITLE_BUF_SIZE	120
+#endif
+
 
 /**
@@ -113,22 +137,69 @@
 
 /**
- * Print the error message pertaining to the specified error code to
- * the log.
- *
- * @param log_level The log will be printed at this log level.
- * @param sender    The log sender string.
- * @param status    The error code which error message will be printed.
- * @param title	    String to be printed before the error message. Note
- *		    that a colon will be added automatically between
- *		    this string and the error message.
- * @param options   Options, currently must be zero.
- */
-#if PJ_LOG_MAX_LEVEL >= 1
-PJ_DECL(void) pj_perror(int log_level, const char *sender, 
-			pj_status_t status, const char *title,
-			int options);
-#else
-#   define pj_perror(level, sender, status, title, options)
-#endif	/* #if PJ_LOG_MAX_LEVEL >= 1 */
+ * A utility macro to print error message pertaining to the specified error 
+ * code to the log. This macro will construct the error message title 
+ * according to the 'title_fmt' argument, and add the error string pertaining
+ * to the error code after the title string. A colon (':') will be added 
+ * automatically between the title and the error string.
+ *
+ * This function is similar to pj_perror() function, but has the advantage
+ * that the function call can be omitted from the link process if the
+ * log level argument is below PJ_LOG_MAX_LEVEL threshold.
+ *
+ * Note that the title string constructed from the title_fmt will be built on
+ * a string buffer which size is PJ_PERROR_TITLE_BUF_SIZE, which normally is
+ * allocated from the stack. By default this buffer size is small (around
+ * 120 characters). Application MUST ensure that the constructed title string
+ * will not exceed this limit, since not all platforms support truncating
+ * the string.
+ *
+ * @see pj_perror()
+ *
+ * @param level	    The logging verbosity level, valid values are 0-6. Lower
+ *		    number indicates higher importance, with level zero 
+ *		    indicates fatal error. Only numeral argument is 
+ *		    permitted (e.g. not variable).
+ * @param arg	    Enclosed 'printf' like arguments, with the following
+ *		    arguments:
+ *		     - the sender (NULL terminated string),
+ *		     - the error code (pj_status_t)
+ *		     - the format string (title_fmt), and 
+ *		     - optional variable number of arguments suitable for the 
+ *		       format string.
+ *
+ * Sample:
+ * \verbatim
+   PJ_PERROR(2, (__FILE__, PJ_EBUSY, "Error making %s", "coffee"));
+   \endverbatim
+ * @hideinitializer
+ */
+#define PJ_PERROR(level,arg)	do { \
+				    pj_perror_wrapper_##level(arg); \
+				} while (0)
+
+/**
+ * A utility function to print error message pertaining to the specified error 
+ * code to the log. This function will construct the error message title 
+ * according to the 'title_fmt' argument, and add the error string pertaining
+ * to the error code after the title string. A colon (':') will be added 
+ * automatically between the title and the error string.
+ *
+ * Unlike the PJ_PERROR() macro, this function takes the \a log_level argument
+ * as a normal argument, unlike in PJ_PERROR() where a numeral value must be
+ * given. However this function will always be linked to the executable,
+ * unlike PJ_PERROR() which can be omitted when the level is below the 
+ * PJ_LOG_MAX_LEVEL.
+ *
+ * Note that the title string constructed from the title_fmt will be built on
+ * a string buffer which size is PJ_PERROR_TITLE_BUF_SIZE, which normally is
+ * allocated from the stack. By default this buffer size is small (around
+ * 120 characters). Application MUST ensure that the constructed title string
+ * will not exceed this limit, since not all platforms support truncating
+ * the string.
+ *
+ * @see PJ_PERROR()
+ */
+PJ_DECL(void) pj_perror(int log_level, const char *sender, pj_status_t status,
+		        const char *title_fmt, ...);
 
 
@@ -403,4 +474,100 @@
 void pj_errno_clear_handlers(void);
 
+
+/****** Internal for PJ_PERROR *******/
+
+/**
+ * @def pj_perror_wrapper_1(arg)
+ * Internal function to write log with verbosity 1. Will evaluate to
+ * empty expression if PJ_LOG_MAX_LEVEL is below 1.
+ * @param arg       Log expression.
+ */
+#if PJ_LOG_MAX_LEVEL >= 1
+    #define pj_perror_wrapper_1(arg)	pj_perror_1 arg
+    /** Internal function. */
+    PJ_DECL(void) pj_perror_1(const char *sender, pj_status_t status, 
+			      const char *title_fmt, ...);
+#else
+    #define pj_perror_wrapper_1(arg)
+#endif
+
+/**
+ * @def pj_perror_wrapper_2(arg)
+ * Internal function to write log with verbosity 2. Will evaluate to
+ * empty expression if PJ_LOG_MAX_LEVEL is below 2.
+ * @param arg       Log expression.
+ */
+#if PJ_LOG_MAX_LEVEL >= 2
+    #define pj_perror_wrapper_2(arg)	pj_perror_2 arg
+    /** Internal function. */
+    PJ_DECL(void) pj_perror_2(const char *sender, pj_status_t status, 
+			      const char *title_fmt, ...);
+#else
+    #define pj_perror_wrapper_2(arg)
+#endif
+
+/**
+ * @def pj_perror_wrapper_3(arg)
+ * Internal function to write log with verbosity 3. Will evaluate to
+ * empty expression if PJ_LOG_MAX_LEVEL is below 3.
+ * @param arg       Log expression.
+ */
+#if PJ_LOG_MAX_LEVEL >= 3
+    #define pj_perror_wrapper_3(arg)	pj_perror_3 arg
+    /** Internal function. */
+    PJ_DECL(void) pj_perror_3(const char *sender, pj_status_t status, 
+			      const char *title_fmt, ...);
+#else
+    #define pj_perror_wrapper_3(arg)
+#endif
+
+/**
+ * @def pj_perror_wrapper_4(arg)
+ * Internal function to write log with verbosity 4. Will evaluate to
+ * empty expression if PJ_LOG_MAX_LEVEL is below 4.
+ * @param arg       Log expression.
+ */
+#if PJ_LOG_MAX_LEVEL >= 4
+    #define pj_perror_wrapper_4(arg)	pj_perror_4 arg
+    /** Internal function. */
+    PJ_DECL(void) pj_perror_4(const char *sender, pj_status_t status, 
+			      const char *title_fmt, ...);
+#else
+    #define pj_perror_wrapper_4(arg)
+#endif
+
+/**
+ * @def pj_perror_wrapper_5(arg)
+ * Internal function to write log with verbosity 5. Will evaluate to
+ * empty expression if PJ_LOG_MAX_LEVEL is below 5.
+ * @param arg       Log expression.
+ */
+#if PJ_LOG_MAX_LEVEL >= 5
+    #define pj_perror_wrapper_5(arg)	pj_perror_5 arg
+    /** Internal function. */
+    PJ_DECL(void) pj_perror_5(const char *sender, pj_status_t status, 
+			      const char *title_fmt, ...);
+#else
+    #define pj_perror_wrapper_5(arg)
+#endif
+
+/**
+ * @def pj_perror_wrapper_6(arg)
+ * Internal function to write log with verbosity 6. Will evaluate to
+ * empty expression if PJ_LOG_MAX_LEVEL is below 6.
+ * @param arg       Log expression.
+ */
+#if PJ_LOG_MAX_LEVEL >= 6
+    #define pj_perror_wrapper_6(arg)	pj_perror_6 arg
+    /** Internal function. */
+    PJ_DECL(void) pj_perror_6(const char *sender, pj_status_t status, 
+			      const char *title_fmt, ...);
+#else
+    #define pj_perror_wrapper_6(arg)
+#endif
+
+
+
+
 PJ_END_DECL
 
Index: pjproject/trunk/pjlib/src/pj/errno.c
===================================================================
--- pjproject/trunk/pjlib/src/pj/errno.c	(revision 2991)
+++ pjproject/trunk/pjlib/src/pj/errno.c	(revision 2992)
@@ -200,5 +200,5 @@
 
 #if PJ_LOG_MAX_LEVEL >= 1
-static void call_logger(const char *sender, int level, const char *format, ...)
+static void invoke_log(const char *sender, int level, const char *format, ...)
 {
     va_list arg;
@@ -208,20 +208,103 @@
 }
 
-/*
- * perror()
- */
-PJ_DEF(void) pj_perror(int log_level, const char *sender, 
-		       pj_status_t status, const char *title,
-		       int options)
-{
+static void pj_perror_imp(int log_level, const char *sender, 
+			  pj_status_t status,
+		          const char *title_fmt, va_list marker)
+{
+    char titlebuf[PJ_PERROR_TITLE_BUF_SIZE];
     char errmsg[PJ_ERR_MSG_SIZE];
-
-    PJ_ASSERT_ON_FAIL(options==0, return);
-    PJ_UNUSED_ARG(options);
-
+    int len;
+
+    /* Build the title */
+    len = pj_ansi_vsnprintf(titlebuf, sizeof(titlebuf), title_fmt, marker);
+    if (len < 0 || len >= sizeof(titlebuf))
+	pj_ansi_strcpy(titlebuf, "Error");
+
+    /* Get the error */
     pj_strerror(status, errmsg, sizeof(errmsg));
-    call_logger(sender, log_level, "%s: %s", title, errmsg);
+
+    /* Send to log */
+    invoke_log(sender, log_level, "%s: %s", titlebuf, errmsg);
+}
+
+PJ_DEF(void) pj_perror(int log_level, const char *sender, pj_status_t status,
+		       const char *title_fmt, ...)
+{
+    va_list marker;
+    va_start(marker, title_fmt);
+    pj_perror_imp(log_level, sender, status, title_fmt, marker);
+    va_end(marker);
+}
+
+PJ_DEF(void) pj_perror_1(const char *sender, pj_status_t status,
+			 const char *title_fmt, ...)
+{
+    va_list marker;
+    va_start(marker, title_fmt);
+    pj_perror_imp(1, sender, status, title_fmt, marker);
+    va_end(marker);
+}
+
+#else /* #if PJ_LOG_MAX_LEVEL >= 1 */
+PJ_DEF(void) pj_perror(int log_level, const char *sender, pj_status_t status,
+		       const char *title_fmt, ...)
+{
 }
 #endif	/* #if PJ_LOG_MAX_LEVEL >= 1 */
 
 
+#if PJ_LOG_MAX_LEVEL >= 2
+PJ_DEF(void) pj_perror_2(const char *sender, pj_status_t status,
+			 const char *title_fmt, ...)
+{
+    va_list marker;
+    va_start(marker, title_fmt);
+    pj_perror_imp(2, sender, status, title_fmt, marker);
+    va_end(marker);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 3
+PJ_DEF(void) pj_perror_3(const char *sender, pj_status_t status,
+			 const char *title_fmt, ...)
+{
+    va_list marker;
+    va_start(marker, title_fmt);
+    pj_perror_imp(3, sender, status, title_fmt, marker);
+    va_end(marker);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 4
+PJ_DEF(void) pj_perror_4(const char *sender, pj_status_t status,
+			 const char *title_fmt, ...)
+{
+    va_list marker;
+    va_start(marker, title_fmt);
+    pj_perror_imp(4, sender, status, title_fmt, marker);
+    va_end(marker);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 5
+PJ_DEF(void) pj_perror_5(const char *sender, pj_status_t status,
+			 const char *title_fmt, ...)
+{
+    va_list marker;
+    va_start(marker, title_fmt);
+    pj_perror_imp(5, sender, status, title_fmt, marker);
+    va_end(marker);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 6
+PJ_DEF(void) pj_perror_6(const char *sender, pj_status_t status,
+			 const char *title_fmt, ...)
+{
+    va_list marker;
+    va_start(marker, title_fmt);
+    pj_perror_imp(6, sender, status, title_fmt, marker);
+    va_end(marker);
+}
+#endif
+
Index: pjproject/trunk/pjlib/src/pjlib-test/errno.c
===================================================================
--- pjproject/trunk/pjlib/src/pjlib-test/errno.c	(revision 2991)
+++ pjproject/trunk/pjlib/src/pjlib-test/errno.c	(revision 2992)
@@ -159,4 +159,8 @@
               CUT, errbuf));
 
+    /* Perror */
+    pj_perror(3, THIS_FILE, PJ_SUCCESS, "...testing %s", "pj_perror");
+    PJ_PERROR(3,(THIS_FILE, PJ_SUCCESS, "...testing %s", "PJ_PERROR"));
+
     return 0;
 }
