summaryrefslogtreecommitdiff
path: root/pjlib/src/pj
diff options
context:
space:
mode:
Diffstat (limited to 'pjlib/src/pj')
-rw-r--r--pjlib/src/pj/addr_resolv_linux_kernel.c14
-rw-r--r--pjlib/src/pj/addr_resolv_sock.c44
-rw-r--r--pjlib/src/pj/array.c63
-rw-r--r--pjlib/src/pj/compat/longjmp_i386.S42
-rw-r--r--pjlib/src/pj/compat/setjmp_i386.S61
-rw-r--r--pjlib/src/pj/compat/sigjmp.c21
-rw-r--r--pjlib/src/pj/compat/string.c33
-rw-r--r--pjlib/src/pj/config.c40
-rw-r--r--pjlib/src/pj/equeue_winnt.c13
-rw-r--r--pjlib/src/pj/errno.c107
-rw-r--r--pjlib/src/pj/except.c148
-rw-r--r--pjlib/src/pj/extra-exports.c38
-rw-r--r--pjlib/src/pj/fifobuf.c182
-rw-r--r--pjlib/src/pj/guid.c19
-rw-r--r--pjlib/src/pj/guid_simple.c60
-rw-r--r--pjlib/src/pj/guid_win32.c61
-rw-r--r--pjlib/src/pj/hash.c252
-rw-r--r--pjlib/src/pj/ioqueue_dummy.c186
-rw-r--r--pjlib/src/pj/ioqueue_epoll.c852
-rw-r--r--pjlib/src/pj/ioqueue_linux_kernel.c150
-rw-r--r--pjlib/src/pj/ioqueue_select.c947
-rw-r--r--pjlib/src/pj/ioqueue_winnt.c852
-rw-r--r--pjlib/src/pj/list.c18
-rw-r--r--pjlib/src/pj/lock.c190
-rw-r--r--pjlib/src/pj/log.c217
-rw-r--r--pjlib/src/pj/log_writer_printk.c20
-rw-r--r--pjlib/src/pj/log_writer_stdout.c66
-rw-r--r--pjlib/src/pj/md5.c404
-rw-r--r--pjlib/src/pj/os_core_linux_kernel.c685
-rw-r--r--pjlib/src/pj/os_core_unix.c1182
-rw-r--r--pjlib/src/pj/os_core_win32.c1182
-rw-r--r--pjlib/src/pj/os_error_linux_kernel.c73
-rw-r--r--pjlib/src/pj/os_error_unix.c52
-rw-r--r--pjlib/src/pj/os_error_win32.c161
-rw-r--r--pjlib/src/pj/os_time_ansi.c65
-rw-r--r--pjlib/src/pj/os_time_linux_kernel.c58
-rw-r--r--pjlib/src/pj/os_timestamp_common.c129
-rw-r--r--pjlib/src/pj/os_timestamp_linux.c137
-rw-r--r--pjlib/src/pj/os_timestamp_linux_kernel.c70
-rw-r--r--pjlib/src/pj/os_timestamp_win32.c38
-rw-r--r--pjlib/src/pj/pool.c265
-rw-r--r--pjlib/src/pj/pool_caching.c210
-rw-r--r--pjlib/src/pj/pool_dbg_win32.c226
-rw-r--r--pjlib/src/pj/pool_policy_kmalloc.c54
-rw-r--r--pjlib/src/pj/pool_policy_malloc.c58
-rw-r--r--pjlib/src/pj/rand.c29
-rw-r--r--pjlib/src/pj/rbtree.c416
-rw-r--r--pjlib/src/pj/scanner.c556
-rw-r--r--pjlib/src/pj/sock_bsd.c572
-rw-r--r--pjlib/src/pj/sock_linux_kernel.c749
-rw-r--r--pjlib/src/pj/sock_select.c101
-rw-r--r--pjlib/src/pj/string.c124
-rw-r--r--pjlib/src/pj/stun.c118
-rw-r--r--pjlib/src/pj/stun_client.c270
-rw-r--r--pjlib/src/pj/symbols.c404
-rw-r--r--pjlib/src/pj/timer.c504
-rw-r--r--pjlib/src/pj/tounix4
-rw-r--r--pjlib/src/pj/types.c36
-rw-r--r--pjlib/src/pj/xml.c392
59 files changed, 14020 insertions, 0 deletions
diff --git a/pjlib/src/pj/addr_resolv_linux_kernel.c b/pjlib/src/pj/addr_resolv_linux_kernel.c
new file mode 100644
index 00000000..7c085c60
--- /dev/null
+++ b/pjlib/src/pj/addr_resolv_linux_kernel.c
@@ -0,0 +1,14 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/addr_resolv_linux_kernel.c 1 10/05/05 4:41p Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/addr_resolv_linux_kernel.c $
+ *
+ * 1 10/05/05 4:41p Bennylp
+ * Created.
+ *
+ */
+#include <pj/addr_resolv.h>
+
+PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *hostname, pj_hostent *phe)
+{
+ return -1;
+}
+
diff --git a/pjlib/src/pj/addr_resolv_sock.c b/pjlib/src/pj/addr_resolv_sock.c
new file mode 100644
index 00000000..0200c65a
--- /dev/null
+++ b/pjlib/src/pj/addr_resolv_sock.c
@@ -0,0 +1,44 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/addr_resolv_sock.c 2 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/addr_resolv_sock.c $
+ *
+ * 2 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 1 9/22/05 10:38a Bennylp
+ * Created.
+ *
+ */
+#include <pj/addr_resolv.h>
+#include <pj/assert.h>
+#include <pj/string.h>
+#include <pj/compat/socket.h>
+#include <pj/errno.h>
+
+
+PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *hostname, pj_hostent *phe)
+{
+ struct hostent *he;
+ char copy[PJ_MAX_HOSTNAME];
+
+ pj_assert(hostname && hostname ->slen < PJ_MAX_HOSTNAME);
+
+ if (hostname->slen >= PJ_MAX_HOSTNAME)
+ return PJ_ENAMETOOLONG;
+
+ pj_memcpy(copy, hostname->ptr, hostname->slen);
+ copy[ hostname->slen ] = '\0';
+
+ he = gethostbyname(copy);
+ if (!he)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+
+ phe->h_name = he->h_name;
+ phe->h_aliases = he->h_aliases;
+ phe->h_addrtype = he->h_addrtype;
+ phe->h_length = he->h_length;
+ phe->h_addr_list = he->h_addr_list;
+
+ return PJ_SUCCESS;
+}
+
diff --git a/pjlib/src/pj/array.c b/pjlib/src/pj/array.c
new file mode 100644
index 00000000..edb4994d
--- /dev/null
+++ b/pjlib/src/pj/array.c
@@ -0,0 +1,63 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/array.c 5 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/array.c $
+ *
+ * 5 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 4 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/array.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+
+PJ_DEF(void) pj_array_insert( void *array,
+ unsigned elem_size,
+ unsigned count,
+ unsigned pos,
+ const void *value)
+{
+ if (count && pos < count-1) {
+ pj_memmove( (char*)array + (pos+1)*elem_size,
+ (char*)array + pos*elem_size,
+ (count-pos)*elem_size);
+ }
+ pj_memmove((char*)array + pos*elem_size, value, elem_size);
+}
+
+PJ_DEF(void) pj_array_erase( void *array,
+ unsigned elem_size,
+ unsigned count,
+ unsigned pos)
+{
+ pj_assert(count != 0);
+ if (pos < count-1) {
+ pj_memmove( (char*)array + pos*elem_size,
+ (char*)array + (pos+1)*elem_size,
+ (count-pos-1)*elem_size);
+ }
+}
+
+PJ_DEF(pj_status_t) pj_array_find( const void *array,
+ unsigned elem_size,
+ unsigned count,
+ pj_status_t (*matching)(const void *value),
+ void **result)
+{
+ unsigned i;
+ const char *char_array = array;
+ for (i=0; i<count; ++i) {
+ if ( (*matching)(char_array) == PJ_SUCCESS) {
+ if (result) {
+ *result = (void*)char_array;
+ }
+ return PJ_SUCCESS;
+ }
+ char_array += elem_size;
+ }
+ return PJ_ENOTFOUND;
+}
+
diff --git a/pjlib/src/pj/compat/longjmp_i386.S b/pjlib/src/pj/compat/longjmp_i386.S
new file mode 100644
index 00000000..0788f44a
--- /dev/null
+++ b/pjlib/src/pj/compat/longjmp_i386.S
@@ -0,0 +1,42 @@
+/* longjmp for i386.
+ Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#define _ASM
+#define _SETJMP_H
+#define PJ_LINUX_KERNEL 1
+#include <pj/compat/setjmp.h>
+
+.global __longjmp
+.type __longjmp,%function
+.align 4
+__longjmp:
+ movl 4(%esp), %ecx /* User's jmp_buf in %ecx. */
+ movl 8(%esp), %eax /* Second argument is return value. */
+ /* Save the return address now. */
+ movl (JB_PC*4)(%ecx), %edx
+ /* Restore registers. */
+ movl (JB_BX*4)(%ecx), %ebx
+ movl (JB_SI*4)(%ecx), %esi
+ movl (JB_DI*4)(%ecx), %edi
+ movl (JB_BP*4)(%ecx), %ebp
+ movl (JB_SP*4)(%ecx), %esp
+ /* Jump to saved PC. */
+ jmp *%edx
+.size __longjmp,.-__longjmp
+
diff --git a/pjlib/src/pj/compat/setjmp_i386.S b/pjlib/src/pj/compat/setjmp_i386.S
new file mode 100644
index 00000000..6810c554
--- /dev/null
+++ b/pjlib/src/pj/compat/setjmp_i386.S
@@ -0,0 +1,61 @@
+/* setjmp for i386, ELF version.
+ Copyright (C) 1995, 1996, 1997, 2000, 2001 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#define _ASM
+#define _SETJMP_H
+#define PJ_LINUX_KERNEL 1
+#include <pj/compat/setjmp.h>
+
+
+.global __sigsetjmp
+.type __sigsetjmp,%function
+.align 4
+
+__sigsetjmp:
+ movl 4 (%esp), %eax
+ /* Save registers. */
+ movl %ebx, (0 *4)(%eax)
+ movl %esi, (1 *4)(%eax)
+ movl %edi, (2 *4)(%eax)
+ /* Save SP as it will be after we return. */
+ leal 4(%esp), %ecx
+ movl %ecx, (4 *4)(%eax)
+ /* Save PC we are returning to now. */
+ movl 0(%esp), %ecx
+ movl %ecx, (5 *4)(%eax)
+ /* Save caller's frame pointer. */
+ movl %ebp, (3 *4)(%eax)
+
+ /* Make a tail call to __sigjmp_save; it takes the same args. */
+#ifdef __PIC__
+ /* We cannot use the PLT, because it requires that %ebx be set, but
+ we can't save and restore our caller's value. Instead, we do an
+ indirect jump through the GOT, using for the temporary register
+ %ecx, which is call-clobbered. */
+ call .Lhere
+.Lhere:
+ popl %ecx
+ addl $_GLOBAL_OFFSET_TABLE_+[.- .Lhere ], %ecx
+ movl __sigjmp_save @GOT (%ecx), %ecx
+ jmp *%ecx
+#else
+ jmp __sigjmp_save
+#endif
+.size __sigsetjmp,.-__sigsetjmp
+
diff --git a/pjlib/src/pj/compat/sigjmp.c b/pjlib/src/pj/compat/sigjmp.c
new file mode 100644
index 00000000..ead0e363
--- /dev/null
+++ b/pjlib/src/pj/compat/sigjmp.c
@@ -0,0 +1,21 @@
+#include <pj/config.h>
+#include <pj/compat/setjmp.h>
+
+int __sigjmp_save(sigjmp_buf env, int savemask)
+{
+ return 0;
+}
+
+extern int __sigsetjmp(pj_jmp_buf env, int savemask);
+extern void __longjmp(pj_jmp_buf env, int val) __attribute__((noreturn));
+
+PJ_DEF(int) pj_setjmp(pj_jmp_buf env)
+{
+ return __sigsetjmp(env, 0);
+}
+
+PJ_DEF(void) pj_longjmp(pj_jmp_buf env, int val)
+{
+ __longjmp(env, val);
+}
+
diff --git a/pjlib/src/pj/compat/string.c b/pjlib/src/pj/compat/string.c
new file mode 100644
index 00000000..25fd11c9
--- /dev/null
+++ b/pjlib/src/pj/compat/string.c
@@ -0,0 +1,33 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/compat/string.c 1 9/22/05 10:43a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/compat/string.c $
+ *
+ * 1 9/22/05 10:43a Bennylp
+ * Created.
+ *
+ */
+#include <pj/types.h>
+#include <pj/compat/string.h>
+#include <pj/ctype.h>
+
+PJ_DEF(int) strcasecmp(const char *s1, const char *s2)
+{
+ while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) {
+ if (!*s1++)
+ return 0;
+ ++s2;
+ }
+ return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1;
+}
+
+PJ_DEF(int) strncasecmp(const char *s1, const char *s2, int len)
+{
+ if (!len) return 0;
+
+ while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) {
+ if (!*s1++ || --len <= 0)
+ return 0;
+ ++s2;
+ }
+ return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1;
+}
+
diff --git a/pjlib/src/pj/config.c b/pjlib/src/pj/config.c
new file mode 100644
index 00000000..5a08c8dd
--- /dev/null
+++ b/pjlib/src/pj/config.c
@@ -0,0 +1,40 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/config.c 7 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/config.c $
+ *
+ * 7 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 6 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 5 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/config.h>
+#include <pj/log.h>
+
+static const char *id = "config.c";
+const char *PJ_VERSION = "0.3.0-pre1";
+
+PJ_DEF(void) pj_dump_config(void)
+{
+ PJ_LOG(3, (id, "PJLIB (c)2005 Benny Prijono"));
+ PJ_LOG(3, (id, "Dumping configurations:"));
+ PJ_LOG(3, (id, " PJ_VERSION : %s", PJ_VERSION));
+ PJ_LOG(3, (id, " PJ_DEBUG : %d", PJ_DEBUG));
+ PJ_LOG(3, (id, " PJ_FUNCTIONS_ARE_INLINED : %d", PJ_FUNCTIONS_ARE_INLINED));
+ PJ_LOG(3, (id, " PJ_POOL_DEBUG : %d", PJ_POOL_DEBUG));
+ PJ_LOG(3, (id, " PJ_HAS_THREADS : %d", PJ_HAS_THREADS));
+ PJ_LOG(3, (id, " PJ_LOG_MAX_LEVEL : %d", PJ_LOG_MAX_LEVEL));
+ PJ_LOG(3, (id, " PJ_LOG_MAX_SIZE : %d", PJ_LOG_MAX_SIZE));
+ PJ_LOG(3, (id, " PJ_LOG_USE_STACK_BUFFER : %d", PJ_LOG_USE_STACK_BUFFER));
+ PJ_LOG(3, (id, " PJ_HAS_TCP : %d", PJ_HAS_TCP));
+ PJ_LOG(3, (id, " PJ_MAX_HOSTNAME : %d", PJ_MAX_HOSTNAME));
+ PJ_LOG(3, (id, " PJ_HAS_SEMAPHORE : %d", PJ_HAS_SEMAPHORE));
+ PJ_LOG(3, (id, " PJ_HAS_EVENT_OBJ : %d", PJ_HAS_EVENT_OBJ));
+ PJ_LOG(3, (id, " PJ_HAS_HIGH_RES_TIMER : %d", PJ_HAS_HIGH_RES_TIMER));
+ PJ_LOG(3, (id, " PJ_(endianness) : %s", (PJ_IS_BIG_ENDIAN?"big-endian":"little-endian")));
+ PJ_LOG(3, (id, " PJ_IOQUEUE_MAX_HANDLES : %d", PJ_IOQUEUE_MAX_HANDLES));
+}
diff --git a/pjlib/src/pj/equeue_winnt.c b/pjlib/src/pj/equeue_winnt.c
new file mode 100644
index 00000000..b1ed4508
--- /dev/null
+++ b/pjlib/src/pj/equeue_winnt.c
@@ -0,0 +1,13 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/equeue_winnt.c 2 10/14/05 12:26a Bennylp $ */
+/*
+ * $Log: /pjproject-0.3/pjlib/src/pj/equeue_winnt.c $
+ *
+ * 2 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 1 10/05/05 6:19p Bennylp
+ * Created.
+ *
+ */
+#include <pj/equeue.h>
diff --git a/pjlib/src/pj/errno.c b/pjlib/src/pj/errno.c
new file mode 100644
index 00000000..218c789f
--- /dev/null
+++ b/pjlib/src/pj/errno.c
@@ -0,0 +1,107 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/errno.c 2 10/14/05 12:26a Bennylp $ */
+/*
+ * $Log: /pjproject-0.3/pjlib/src/pj/errno.c $
+ *
+ * 2 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 1 10/08/05 9:53a Bennylp
+ * Created.
+ *
+ */
+#include <pj/errno.h>
+#include <pj/string.h>
+#include <pj/compat/sprintf.h>
+
+/* Prototype for platform specific error message, which will be defined
+ * in separate file.
+ */
+extern int platform_strerror( pj_os_err_type code,
+ char *buf, pj_size_t bufsize );
+
+/* PJLIB's own error codes/messages */
+static const struct
+{
+ int code;
+ const char *msg;
+} err_str[] =
+{
+ { PJ_EUNKNOWN, "Unknown Error" },
+ { PJ_EPENDING, "Pending operation" },
+ { PJ_ETOOMANYCONN, "Too many connecting sockets" },
+ { PJ_EINVAL, "Invalid value or argument" },
+ { PJ_ENAMETOOLONG, "Name too long" },
+ { PJ_ENOTFOUND, "Not found" },
+ { PJ_ENOMEM, "Not enough memory" },
+ { PJ_EBUG, "BUG DETECTED!" },
+ { PJ_ETIMEDOUT, "Operation timed out" },
+ { PJ_ETOOMANY, "Too many objects of the specified type"},
+ { PJ_EBUSY, "Object is busy"},
+ { PJ_ENOTSUP, "Option/operation is not supported"},
+ { PJ_EINVALIDOP, "Invalid operation"}
+};
+
+/*
+ * pjlib_error()
+ *
+ * Retrieve message string for PJLIB's own error code.
+ */
+static int pjlib_error(pj_status_t code, char *buf, pj_size_t size)
+{
+ unsigned i;
+
+ for (i=0; i<sizeof(err_str)/sizeof(err_str[0]); ++i) {
+ if (err_str[i].code == code) {
+ pj_size_t len = strlen(err_str[i].msg);
+ if (len >= size) len = size-1;
+ pj_memcpy(buf, err_str[i].msg, len);
+ buf[len] = '\0';
+ return len;
+ }
+ }
+
+ *buf++ = '?';
+ *buf++ = '?';
+ *buf++ = '?';
+ *buf++ = '\0';
+ return 3;
+}
+
+/*
+ * pj_strerror()
+ */
+PJ_DEF(pj_str_t) pj_strerror( pj_status_t statcode,
+ char *buf, pj_size_t bufsize )
+{
+ int len = -1;
+ pj_str_t errstr;
+
+ if (statcode < PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE) {
+ len = pj_snprintf( buf, bufsize, "Unknown error %d", statcode);
+
+ } else if (statcode < PJ_ERRNO_START_STATUS + PJ_ERRNO_SPACE_SIZE) {
+ len = pjlib_error(statcode, buf, bufsize);
+
+ } else if (statcode < PJ_ERRNO_START_SYS + PJ_ERRNO_SPACE_SIZE) {
+ 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 {
+ len = pj_snprintf( buf, bufsize, "Invalid error %d", statcode);
+
+ }
+
+ if (len < 1) {
+ *buf = '\0';
+ len = 0;
+ }
+
+ errstr.ptr = buf;
+ errstr.slen = len;
+
+ return errstr;
+}
+
diff --git a/pjlib/src/pj/except.c b/pjlib/src/pj/except.c
new file mode 100644
index 00000000..0525caee
--- /dev/null
+++ b/pjlib/src/pj/except.c
@@ -0,0 +1,148 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/except.c 6 10/14/05 12:26a Bennylp $ */
+/*
+ * $Log: /pjproject-0.3/pjlib/src/pj/except.c $
+ *
+ * 6 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 5 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 4 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/except.h>
+#include <pj/os.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/errno.h>
+
+static long thread_local_id = -1;
+
+#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0
+ static const char *exception_id_names[PJ_MAX_EXCEPTION_ID];
+#else
+ /*
+ * Start from 1 (not 0)!!!
+ * Exception 0 is reserved for normal path of setjmp()!!!
+ */
+ static int last_exception_id = 1;
+#endif /* PJ_HAS_EXCEPTION_NAMES */
+
+
+PJ_DEF(void) pj_throw_exception_(int exception_id)
+{
+ struct pj_exception_state_t *handler;
+
+ handler = pj_thread_local_get(thread_local_id);
+ if (handler == NULL) {
+ PJ_LOG(1,("except.c", "!!!FATAL: unhandled exception %d!\n", exception_id));
+ pj_assert(handler != NULL);
+ /* This will crash the system! */
+ }
+ pj_longjmp(handler->state, exception_id);
+}
+
+PJ_DEF(void) pj_push_exception_handler_(struct pj_exception_state_t *rec)
+{
+ struct pj_exception_state_t *parent_handler = NULL;
+
+ if (thread_local_id == -1) {
+ pj_thread_local_alloc(&thread_local_id);
+ pj_assert(thread_local_id != -1);
+ }
+ parent_handler = pj_thread_local_get(thread_local_id);
+ rec->prev = parent_handler;
+ pj_thread_local_set(thread_local_id, rec);
+}
+
+PJ_DEF(void) pj_pop_exception_handler_(void)
+{
+ struct pj_exception_state_t *handler;
+
+ handler = pj_thread_local_get(thread_local_id);
+ pj_assert(handler != NULL);
+ pj_thread_local_set(thread_local_id, handler->prev);
+}
+
+#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0
+PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name,
+ pj_exception_id_t *id)
+{
+ unsigned i;
+
+ pj_enter_critical_section();
+
+ /*
+ * Start from 1 (not 0)!!!
+ * Exception 0 is reserved for normal path of setjmp()!!!
+ */
+ for (i=1; i<PJ_MAX_EXCEPTION_ID; ++i) {
+ if (exception_id_names[i] == NULL) {
+ exception_id_names[i] = name;
+ *id = i;
+ pj_leave_critical_section();
+ return PJ_SUCCESS;
+ }
+ }
+
+ pj_leave_critical_section();
+ return PJ_ETOOMANY;
+}
+
+PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id )
+{
+ /*
+ * Start from 1 (not 0)!!!
+ * Exception 0 is reserved for normal path of setjmp()!!!
+ */
+ PJ_ASSERT_RETURN(id>0 && id<PJ_MAX_EXCEPTION_ID, PJ_EINVAL);
+
+ pj_enter_critical_section();
+ exception_id_names[id] = NULL;
+ pj_leave_critical_section();
+
+ return PJ_SUCCESS;
+
+}
+
+PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id)
+{
+ /*
+ * Start from 1 (not 0)!!!
+ * Exception 0 is reserved for normal path of setjmp()!!!
+ */
+ PJ_ASSERT_RETURN(id>0 && id<PJ_MAX_EXCEPTION_ID, "<Invalid ID>");
+
+ if (exception_id_names[id] == NULL)
+ return "<Unallocated ID>";
+
+ return exception_id_names[id];
+}
+
+#else /* PJ_HAS_EXCEPTION_NAMES */
+PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name,
+ pj_exception_id_t *id)
+{
+ PJ_ASSERT_RETURN(last_exception_id < PJ_MAX_EXCEPTION_ID-1, PJ_ETOOMANY);
+
+ *id = last_exception_id++
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id )
+{
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id)
+{
+ return "";
+}
+
+#endif /* PJ_HAS_EXCEPTION_NAMES */
+
+
+
diff --git a/pjlib/src/pj/extra-exports.c b/pjlib/src/pj/extra-exports.c
new file mode 100644
index 00000000..4cc12e3f
--- /dev/null
+++ b/pjlib/src/pj/extra-exports.c
@@ -0,0 +1,38 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/extra-exports.c 1 10/29/05 11:56a Bennylp $ */
+/*
+ * $Log: /pjproject-0.3/pjlib/src/pj/extra-exports.c $
+ *
+ * 1 10/29/05 11:56a Bennylp
+ * Version 0.3-pre2
+ *
+ */
+
+/*
+ * This file contains code to export extra symbols from Linux kernel.
+ * It should be copied to Linux kernel source tree and added to
+ * Linux kernel combilation.
+ *
+ * This file is part of PJLIB project.
+ */
+#include <linux/module.h>
+#include <linux/syscalls.h>
+
+EXPORT_SYMBOL(sys_select);
+
+EXPORT_SYMBOL(sys_epoll_create);
+EXPORT_SYMBOL(sys_epoll_ctl);
+EXPORT_SYMBOL(sys_epoll_wait);
+
+EXPORT_SYMBOL(sys_socket);
+EXPORT_SYMBOL(sys_bind);
+EXPORT_SYMBOL(sys_getpeername);
+EXPORT_SYMBOL(sys_getsockname);
+EXPORT_SYMBOL(sys_sendto);
+EXPORT_SYMBOL(sys_recvfrom);
+EXPORT_SYMBOL(sys_getsockopt);
+EXPORT_SYMBOL(sys_setsockopt);
+EXPORT_SYMBOL(sys_listen);
+EXPORT_SYMBOL(sys_shutdown);
+EXPORT_SYMBOL(sys_connect);
+EXPORT_SYMBOL(sys_accept);
+
diff --git a/pjlib/src/pj/fifobuf.c b/pjlib/src/pj/fifobuf.c
new file mode 100644
index 00000000..d0b41a15
--- /dev/null
+++ b/pjlib/src/pj/fifobuf.c
@@ -0,0 +1,182 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/fifobuf.c 4 9/17/05 10:37a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/fifobuf.c $
+ *
+ * 4 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/fifobuf.h>
+#include <pj/log.h>
+#include <pj/assert.h>
+#include <pj/os.h>
+
+#define THIS_FILE "fifobuf"
+
+#define SZ sizeof(unsigned)
+
+PJ_DEF(void)
+pj_fifobuf_init (pj_fifobuf_t *fifobuf, void *buffer, unsigned size)
+{
+ PJ_CHECK_STACK();
+
+ PJ_LOG(6, (THIS_FILE,
+ "fifobuf_init fifobuf=%p buffer=%p, size=%d",
+ fifobuf, buffer, size));
+
+ fifobuf->first = buffer;
+ fifobuf->last = fifobuf->first + size;
+ fifobuf->ubegin = fifobuf->uend = fifobuf->first;
+ fifobuf->full = 0;
+}
+
+PJ_DEF(unsigned)
+pj_fifobuf_max_size (pj_fifobuf_t *fifobuf)
+{
+ unsigned s1, s2;
+
+ PJ_CHECK_STACK();
+
+ if (fifobuf->uend >= fifobuf->ubegin) {
+ s1 = fifobuf->last - fifobuf->uend;
+ s2 = fifobuf->ubegin - fifobuf->first;
+ } else {
+ s1 = s2 = fifobuf->ubegin - fifobuf->uend;
+ }
+
+ return s1<s2 ? s2 : s1;
+}
+
+PJ_DEF(void*)
+pj_fifobuf_alloc (pj_fifobuf_t *fifobuf, unsigned size)
+{
+ unsigned available;
+ char *start;
+
+ PJ_CHECK_STACK();
+
+ if (fifobuf->full) {
+ PJ_LOG(6, (THIS_FILE,
+ "fifobuf_alloc fifobuf=%p, size=%d: full!",
+ fifobuf, size));
+ return NULL;
+ }
+
+ /* try to allocate from the end part of the fifo */
+ if (fifobuf->uend >= fifobuf->ubegin) {
+ available = fifobuf->last - fifobuf->uend;
+ if (available >= size+SZ) {
+ char *ptr = fifobuf->uend;
+ fifobuf->uend += (size+SZ);
+ if (fifobuf->uend == fifobuf->last)
+ fifobuf->uend = fifobuf->first;
+ if (fifobuf->uend == fifobuf->ubegin)
+ fifobuf->full = 1;
+ *(unsigned*)ptr = size+SZ;
+ ptr += SZ;
+
+ PJ_LOG(6, (THIS_FILE,
+ "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p",
+ fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend));
+ return ptr;
+ }
+ }
+
+ /* try to allocate from the start part of the fifo */
+ start = (fifobuf->uend <= fifobuf->ubegin) ? fifobuf->uend : fifobuf->first;
+ available = fifobuf->ubegin - start;
+ if (available >= size+SZ) {
+ char *ptr = start;
+ fifobuf->uend = start + size + SZ;
+ if (fifobuf->uend == fifobuf->ubegin)
+ fifobuf->full = 1;
+ *(unsigned*)ptr = size+SZ;
+ ptr += SZ;
+
+ PJ_LOG(6, (THIS_FILE,
+ "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p",
+ fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend));
+ return ptr;
+ }
+
+ PJ_LOG(6, (THIS_FILE,
+ "fifobuf_alloc fifobuf=%p, size=%d: no space left! p1=%p, p2=%p",
+ fifobuf, size, fifobuf->ubegin, fifobuf->uend));
+ return NULL;
+}
+
+PJ_DEF(pj_status_t)
+pj_fifobuf_unalloc (pj_fifobuf_t *fifobuf, void *buf)
+{
+ char *ptr = buf;
+ char *endptr;
+ unsigned sz;
+
+ PJ_CHECK_STACK();
+
+ ptr -= SZ;
+ sz = *(unsigned*)ptr;
+
+ endptr = fifobuf->uend;
+ if (endptr == fifobuf->first)
+ endptr = fifobuf->last;
+
+ if (ptr+sz != endptr) {
+ pj_assert(!"Invalid pointer to undo alloc");
+ return -1;
+ }
+
+ fifobuf->uend = ptr;
+ fifobuf->full = 0;
+
+ PJ_LOG(6, (THIS_FILE,
+ "fifobuf_unalloc fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p",
+ fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend));
+
+ return 0;
+}
+
+PJ_DEF(pj_status_t)
+pj_fifobuf_free (pj_fifobuf_t *fifobuf, void *buf)
+{
+ char *ptr = buf;
+ char *end;
+ unsigned sz;
+
+ PJ_CHECK_STACK();
+
+ ptr -= SZ;
+ if (ptr < fifobuf->first || ptr >= fifobuf->last) {
+ pj_assert(!"Invalid pointer to free");
+ return -1;
+ }
+
+ if (ptr != fifobuf->ubegin && ptr != fifobuf->first) {
+ pj_assert(!"Invalid free() sequence!");
+ return -1;
+ }
+
+ end = (fifobuf->uend > fifobuf->ubegin) ? fifobuf->uend : fifobuf->last;
+ sz = *(unsigned*)ptr;
+ if (ptr+sz > end) {
+ pj_assert(!"Invalid size!");
+ return -1;
+ }
+
+ fifobuf->ubegin = ptr + sz;
+
+ /* Rollover */
+ if (fifobuf->ubegin == fifobuf->last)
+ fifobuf->ubegin = fifobuf->first;
+
+ /* Reset if fifobuf is empty */
+ if (fifobuf->ubegin == fifobuf->uend)
+ fifobuf->ubegin = fifobuf->uend = fifobuf->first;
+
+ fifobuf->full = 0;
+
+ PJ_LOG(6, (THIS_FILE,
+ "fifobuf_free fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p",
+ fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend));
+
+ return 0;
+}
diff --git a/pjlib/src/pj/guid.c b/pjlib/src/pj/guid.c
new file mode 100644
index 00000000..20cb72a7
--- /dev/null
+++ b/pjlib/src/pj/guid.c
@@ -0,0 +1,19 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/guid.c 12 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/guid.c $
+ *
+ * 12 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 11 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/guid.h>
+#include <pj/pool.h>
+
+PJ_DEF(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str)
+{
+ str->ptr = pj_pool_alloc(pool, PJ_GUID_STRING_LENGTH);
+ pj_generate_unique_string(str);
+}
diff --git a/pjlib/src/pj/guid_simple.c b/pjlib/src/pj/guid_simple.c
new file mode 100644
index 00000000..b5cca310
--- /dev/null
+++ b/pjlib/src/pj/guid_simple.c
@@ -0,0 +1,60 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/guid_simple.c 3 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/guid_simple.c $
+ *
+ * 3 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 2 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/guid.h>
+#include <pj/os.h>
+#include <pj/rand.h>
+#include <pj/string.h>
+#include <pj/compat/sprintf.h>
+
+const unsigned PJ_GUID_STRING_LENGTH=20;
+
+static void init_mac_address(unsigned char mac_addr[16])
+{
+ unsigned long *ulval1 = (unsigned long*) &mac_addr[0];
+ unsigned short *usval1 = (unsigned short*) &mac_addr[4];
+
+ *ulval1 = pj_rand();
+ *usval1 = (unsigned short) pj_rand();
+}
+
+PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str)
+{
+ static int guid_initialized;
+ static unsigned pid;
+ static char str_pid[5];
+ static unsigned char mac_addr[6];
+ static char str_mac_addr[16];
+ static unsigned clock_seq;
+
+ PJ_CHECK_STACK();
+
+ if (guid_initialized == 0) {
+ pid = pj_getpid();
+ init_mac_address(mac_addr);
+ clock_seq = 0;
+
+ sprintf(str_pid, "%04x", pid);
+ sprintf(str_mac_addr, "%02x%02x%02x%02x%02x%02x",
+ mac_addr[0], mac_addr[1], mac_addr[2],
+ mac_addr[3], mac_addr[4], mac_addr[5]);
+
+ guid_initialized = 1;
+ }
+
+ strcpy(str->ptr, str_pid);
+ sprintf(str->ptr+4, "%04x", clock_seq++);
+ pj_memcpy(str->ptr+8, str_mac_addr, 12);
+ str->slen = 20;
+
+ return str;
+}
+
diff --git a/pjlib/src/pj/guid_win32.c b/pjlib/src/pj/guid_win32.c
new file mode 100644
index 00000000..8e3707ec
--- /dev/null
+++ b/pjlib/src/pj/guid_win32.c
@@ -0,0 +1,61 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/guid_win32.c 4 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/guid_win32.c $
+ *
+ * 4 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 3 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/guid.h>
+#include <pj/string.h>
+#include <pj/sock.h>
+#include <windows.h>
+#include <objbase.h>
+#include <pj/os.h>
+
+
+const unsigned PJ_GUID_STRING_LENGTH=32;
+
+PJ_INLINE(void) hex2digit(unsigned value, char *p)
+{
+ static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+ *p++ = hex[ (value & 0xF0) >> 4 ];
+ *p++ = hex[ (value & 0x0F) ];
+}
+
+static void guid_to_str( const GUID *guid, pj_str_t *str )
+{
+ unsigned i;
+ GUID guid_copy;
+ const unsigned char *src = (const unsigned char*)&guid_copy;
+ char *dst = str->ptr;
+
+ pj_memcpy(&guid_copy, guid, sizeof(*guid));
+ guid_copy.Data1 = pj_ntohl(guid_copy.Data1);
+ guid_copy.Data2 = pj_ntohs(guid_copy.Data2);
+ guid_copy.Data3 = pj_ntohs(guid_copy.Data3);
+
+ for (i=0; i<16; ++i) {
+ hex2digit( *src, dst );
+ dst += 2;
+ ++src;
+ }
+ str->slen = 32;
+}
+
+
+PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str)
+{
+ GUID guid;
+
+ PJ_CHECK_STACK();
+
+ CoCreateGuid(&guid);
+ guid_to_str( &guid, str );
+ return str;
+}
+
diff --git a/pjlib/src/pj/hash.c b/pjlib/src/pj/hash.c
new file mode 100644
index 00000000..01bfaa6b
--- /dev/null
+++ b/pjlib/src/pj/hash.c
@@ -0,0 +1,252 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/hash.c 8 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/hash.c $
+ *
+ * 8 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 7 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/hash.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+#include <pj/os.h>
+
+/**
+ * The hash multiplier used to calculate hash value.
+ */
+#define PJ_HASH_MULTIPLIER 33
+
+
+struct pj_hash_entry
+{
+ struct pj_hash_entry *next;
+ const void *key;
+ pj_uint32_t hash;
+ pj_uint32_t keylen;
+ void *value;
+};
+
+
+struct pj_hash_table_t
+{
+ pj_hash_entry **table;
+ unsigned count, rows;
+ pj_hash_iterator_t iterator;
+};
+
+
+
+PJ_DEF(pj_uint32_t) pj_hash_calc(pj_uint32_t hash, const void *key, unsigned keylen)
+{
+ PJ_CHECK_STACK();
+
+ if (keylen==PJ_HASH_KEY_STRING) {
+ const unsigned char *p = key;
+ for ( ; *p; ++p ) {
+ hash = hash * PJ_HASH_MULTIPLIER + *p;
+ }
+ keylen = p - (const unsigned char*)key;
+ } else {
+ const unsigned char *p = key,
+ *end = p + keylen;
+ for ( ; p!=end; ++p) {
+ hash = hash * PJ_HASH_MULTIPLIER + *p;
+ }
+ }
+ return hash;
+}
+
+
+PJ_DEF(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size)
+{
+ pj_hash_table_t *h;
+ unsigned table_size;
+
+ h = pj_pool_alloc(pool, sizeof(pj_hash_table_t));
+ h->count = 0;
+
+ PJ_LOG( 5, ("hashtbl", "hash table %p created from pool %s", h, pj_pool_getobjname(pool)));
+
+ /* size must be 2^n - 1.
+ round-up the size to this rule, except when size is 2^n, then size
+ will be round-down to 2^n-1.
+ */
+ table_size = 8;
+ do {
+ table_size <<= 1;
+ } while (table_size <= size);
+ table_size -= 1;
+
+ h->rows = table_size;
+ h->table = pj_pool_calloc(pool, table_size+1, sizeof(pj_hash_entry*));
+ return h;
+}
+
+static pj_hash_entry **find_entry( pj_pool_t *pool, pj_hash_table_t *ht,
+ const void *key, unsigned keylen,
+ void *val)
+{
+ pj_uint32_t hash;
+ pj_hash_entry **p_entry, *entry;
+
+ hash=0;
+ if (keylen==PJ_HASH_KEY_STRING) {
+ const unsigned char *p = key;
+ for ( ; *p; ++p ) {
+ hash = hash * PJ_HASH_MULTIPLIER + *p;
+ }
+ keylen = p - (const unsigned char*)key;
+ } else {
+ const unsigned char *p = key,
+ *end = p + keylen;
+ for ( ; p!=end; ++p) {
+ hash = hash * PJ_HASH_MULTIPLIER + *p;
+ }
+ }
+
+ /* scan the linked list */
+ for (p_entry = &ht->table[hash & ht->rows], entry=*p_entry;
+ entry;
+ p_entry = &entry->next, entry = *p_entry)
+ {
+ if (entry->hash==hash && entry->keylen==keylen &&
+ memcmp(entry->key, key, keylen)==0)
+ {
+ break;
+ }
+ }
+
+ if (entry || val==NULL)
+ return p_entry;
+
+ /* create a new entry */
+ entry = pj_pool_alloc(pool, sizeof(pj_hash_entry));
+ PJ_LOG(5, ("hashtbl", "%p: New p_entry %p created, pool used=%u, cap=%u", ht, entry,
+ pj_pool_get_used_size(pool), pj_pool_get_capacity(pool)));
+ entry->next = NULL;
+ entry->hash = hash;
+ entry->key = key;
+ entry->keylen = keylen;
+ entry->value = val;
+ *p_entry = entry;
+
+ ++ht->count;
+
+ return p_entry;
+}
+
+PJ_DEF(void *) pj_hash_get( pj_hash_table_t *ht,
+ const void *key, unsigned keylen )
+{
+ pj_hash_entry *entry;
+ entry = *find_entry( NULL, ht, key, keylen, NULL);
+ return entry ? entry->value : NULL;
+}
+
+PJ_DEF(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht,
+ const void *key, unsigned keylen,
+ void *value )
+{
+ pj_hash_entry **p_entry;
+
+ p_entry = find_entry( pool, ht, key, keylen, value );
+ if (*p_entry) {
+ if (value == NULL) {
+ /* delete entry */
+ PJ_LOG(5, ("hashtbl", "%p: p_entry %p deleted", ht, *p_entry));
+ *p_entry = (*p_entry)->next;
+ --ht->count;
+
+ } else {
+ /* overwrite */
+ (*p_entry)->value = value;
+ PJ_LOG(5, ("hashtbl", "%p: p_entry %p value set to %p", ht, *p_entry, value));
+ }
+ }
+}
+
+PJ_DEF(unsigned) pj_hash_count( pj_hash_table_t *ht )
+{
+ return ht->count;
+}
+
+PJ_DEF(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht,
+ pj_hash_iterator_t *it )
+{
+ it->index = 0;
+ it->entry = NULL;
+
+ for (; it->index < ht->rows; ++it->index) {
+ it->entry = ht->table[it->index];
+ if (it->entry) {
+ break;
+ }
+ }
+
+ return it->entry ? it : NULL;
+}
+
+PJ_DEF(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht,
+ pj_hash_iterator_t *it )
+{
+ it->entry = it->entry->next;
+ if (it->entry) {
+ return it;
+ }
+
+ for (++it->index; it->index < ht->rows; ++it->index) {
+ it->entry = ht->table[it->index];
+ if (it->entry) {
+ break;
+ }
+ }
+
+ return it->entry ? it : NULL;
+}
+
+PJ_DEF(void*) pj_hash_this( pj_hash_table_t *ht, pj_hash_iterator_t *it )
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(ht);
+ return it->entry->value;
+}
+
+#if 0
+void pj_hash_dump_collision( pj_hash_table_t *ht )
+{
+ unsigned min=0xFFFFFFFF, max=0;
+ unsigned i;
+ char line[120];
+ int len, totlen = 0;
+
+ for (i=0; i<ht->rows; ++i) {
+ unsigned count = 0;
+ pj_hash_entry *entry = ht->table[i];
+ while (entry) {
+ ++count;
+ entry = entry->next;
+ }
+ if (count < min)
+ min = count;
+ if (count > max)
+ max = count;
+ len = pj_snprintf( line+totlen, sizeof(line)-totlen, "%3d:%3d ", i, count);
+ if (len < 1)
+ break;
+ totlen += len;
+
+ if ((i+1) % 10 == 0) {
+ line[totlen] = '\0';
+ PJ_LOG(4,(__FILE__, line));
+ }
+ }
+
+ PJ_LOG(4,(__FILE__,"Count: %d, min: %d, max: %d\n", ht->count, min, max));
+}
+#endif
+
+
diff --git a/pjlib/src/pj/ioqueue_dummy.c b/pjlib/src/pj/ioqueue_dummy.c
new file mode 100644
index 00000000..63abc15b
--- /dev/null
+++ b/pjlib/src/pj/ioqueue_dummy.c
@@ -0,0 +1,186 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/ioqueue_dummy.c 2 10/29/05 11:31a Bennylp $ */
+/*
+ * $Log: /pjproject-0.3/pjlib/src/pj/ioqueue_dummy.c $
+ *
+ * 2 10/29/05 11:31a Bennylp
+ * Changed accept and lock.
+ *
+ * 1 10/23/05 12:53p Bennylp
+ * Created.
+ *
+ */
+#include <pj/ioqueue.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/list.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/sock.h>
+#include <pj/errno.h>
+
+#define THIS_FILE "ioqueue"
+
+#define PJ_IOQUEUE_IS_READ_OP(op) \
+ ((op & PJ_IOQUEUE_OP_READ) || (op & PJ_IOQUEUE_OP_RECV_FROM))
+#define PJ_IOQUEUE_IS_WRITE_OP(op) \
+ ((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO))
+
+
+#if PJ_HAS_TCP
+# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT)
+# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT)
+#else
+# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0
+# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0
+#endif
+
+#if defined(PJ_DEBUG) && PJ_DEBUG != 0
+# define VALIDATE_FD_SET 1
+#else
+# define VALIDATE_FD_SET 0
+#endif
+
+struct pj_ioqueue_key_t
+{
+ PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t)
+ pj_sock_t fd;
+ pj_ioqueue_operation_e op;
+ void *user_data;
+ pj_ioqueue_callback cb;
+};
+
+struct pj_ioqueue_t
+{
+};
+
+PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
+ pj_size_t max_fd,
+ int max_threads,
+ pj_ioqueue_t **ptr_ioqueue)
+{
+ return PJ_ENOTSUP;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque)
+{
+ return PJ_ENOTSUP;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque,
+ pj_lock_t *lock,
+ pj_bool_t auto_delete )
+{
+ return PJ_ENOTSUP;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
+ pj_ioqueue_t *ioque,
+ pj_sock_t sock,
+ void *user_data,
+ const pj_ioqueue_callback *cb,
+ pj_ioqueue_key_t **ptr_key)
+{
+ return PJ_ENOTSUP;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key)
+{
+ return PJ_ENOTSUP;
+}
+
+PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
+{
+ return NULL;
+}
+
+
+PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout)
+{
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen)
+{
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen,
+ unsigned flags)
+{
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen,
+ unsigned flags,
+ pj_sockaddr_t *addr,
+ int *addrlen)
+{
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen)
+{
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen,
+ unsigned flags)
+{
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen,
+ unsigned flags,
+ const pj_sockaddr_t *addr,
+ int addrlen)
+{
+ return -1;
+}
+
+#if PJ_HAS_TCP
+/*
+ * Initiate overlapped accept() operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_t *ioqueue,
+ pj_ioqueue_key_t *key,
+ pj_sock_t *new_sock,
+ pj_sockaddr_t *local,
+ pj_sockaddr_t *remote,
+ int *addrlen)
+{
+ return -1;
+}
+
+/*
+ * Initiate overlapped connect() operation (well, it's non-blocking actually,
+ * since there's no overlapped version of connect()).
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue,
+ pj_ioqueue_key_t *key,
+ const pj_sockaddr_t *addr,
+ int addrlen )
+{
+ return -1;
+}
+#endif /* PJ_HAS_TCP */
+
diff --git a/pjlib/src/pj/ioqueue_epoll.c b/pjlib/src/pj/ioqueue_epoll.c
new file mode 100644
index 00000000..7bbfe135
--- /dev/null
+++ b/pjlib/src/pj/ioqueue_epoll.c
@@ -0,0 +1,852 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/ioqueue_epoll.c 4 10/29/05 10:27p Bennylp $ */
+/*
+ * $Log: /pjproject-0.3/pjlib/src/pj/ioqueue_epoll.c $
+ *
+ * 4 10/29/05 10:27p Bennylp
+ * Fixed misc warnings.
+ *
+ * 3 10/29/05 11:49a Bennylp
+ * Fixed warnings.
+ *
+ * 2 10/29/05 11:31a Bennylp
+ * Changed accept and lock.
+ *
+ * 1 10/17/05 10:49p Bennylp
+ * Created.
+ *
+ */
+
+/*
+ * ioqueue_epoll.c
+ *
+ * This is the implementation of IOQueue framework using /dev/epoll
+ * API in _both_ Linux user-mode and kernel-mode.
+ */
+
+#include <pj/ioqueue.h>
+#include <pj/os.h>
+#include <pj/lock.h>
+#include <pj/log.h>
+#include <pj/list.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/sock.h>
+#include <pj/compat/socket.h>
+
+#if !defined(PJ_LINUX_KERNEL) || PJ_LINUX_KERNEL==0
+ /*
+ * Linux user mode
+ */
+# include <sys/epoll.h>
+# include <errno.h>
+# include <unistd.h>
+
+# define epoll_data data.ptr
+# define epoll_data_type void*
+# define ioctl_val_type unsigned long*
+# define getsockopt_val_ptr int*
+# define os_getsockopt getsockopt
+# define os_ioctl ioctl
+# define os_read read
+# define os_close close
+# define os_epoll_create epoll_create
+# define os_epoll_ctl epoll_ctl
+# define os_epoll_wait epoll_wait
+#else
+ /*
+ * Linux kernel mode.
+ */
+# include <linux/config.h>
+# include <linux/version.h>
+# if defined(MODVERSIONS)
+# include <linux/modversions.h>
+# endif
+# include <linux/kernel.h>
+# include <linux/poll.h>
+# include <linux/eventpoll.h>
+# include <linux/syscalls.h>
+# include <linux/errno.h>
+# include <linux/unistd.h>
+# include <asm/ioctls.h>
+ enum EPOLL_EVENTS
+ {
+ EPOLLIN = 0x001,
+ EPOLLOUT = 0x004,
+ EPOLLERR = 0x008,
+ };
+# define os_epoll_create sys_epoll_create
+ static int os_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
+ {
+ long rc;
+ mm_segment_t oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ rc = sys_epoll_ctl(epfd, op, fd, event);
+ set_fs(oldfs);
+ if (rc) {
+ errno = -rc;
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ static int os_epoll_wait(int epfd, struct epoll_event *events,
+ int maxevents, int timeout)
+ {
+ int count;
+ mm_segment_t oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ count = sys_epoll_wait(epfd, events, maxevents, timeout);
+ set_fs(oldfs);
+ return count;
+ }
+# define os_close sys_close
+# define os_getsockopt pj_sock_getsockopt
+ static int os_read(int fd, void *buf, size_t len)
+ {
+ long rc;
+ mm_segment_t oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ rc = sys_read(fd, buf, len);
+ set_fs(oldfs);
+ if (rc) {
+ errno = -rc;
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+# define socklen_t unsigned
+# define ioctl_val_type unsigned long
+ int ioctl(int fd, int opt, ioctl_val_type value);
+ static int os_ioctl(int fd, int opt, ioctl_val_type value)
+ {
+ int rc;
+ mm_segment_t oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ rc = ioctl(fd, opt, value);
+ set_fs(oldfs);
+ if (rc < 0) {
+ errno = -rc;
+ return rc;
+ } else
+ return rc;
+ }
+# define getsockopt_val_ptr char*
+
+# define epoll_data data
+# define epoll_data_type __u32
+#endif
+
+#define THIS_FILE "ioq_epoll"
+
+#define PJ_IOQUEUE_IS_READ_OP(op) ((op & PJ_IOQUEUE_OP_READ) || \
+ (op & PJ_IOQUEUE_OP_RECV) || \
+ (op & PJ_IOQUEUE_OP_RECV_FROM))
+#define PJ_IOQUEUE_IS_WRITE_OP(op) ((op & PJ_IOQUEUE_OP_WRITE) || \
+ (op & PJ_IOQUEUE_OP_SEND) || \
+ (op & PJ_IOQUEUE_OP_SEND_TO))
+
+
+#if PJ_HAS_TCP
+# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT)
+# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT)
+#else
+# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0
+# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0
+#endif
+
+
+//#define TRACE_(expr) PJ_LOG(3,expr)
+#define TRACE_(expr)
+
+
+/*
+ * This describes each key.
+ */
+struct pj_ioqueue_key_t
+{
+ PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t)
+ pj_sock_t fd;
+ pj_ioqueue_operation_e op;
+ void *user_data;
+ pj_ioqueue_callback cb;
+
+ void *rd_buf;
+ unsigned rd_flags;
+ pj_size_t rd_buflen;
+ void *wr_buf;
+ pj_size_t wr_buflen;
+
+ pj_sockaddr_t *rmt_addr;
+ int *rmt_addrlen;
+
+ pj_sockaddr_t *local_addr;
+ int *local_addrlen;
+
+ pj_sock_t *accept_fd;
+};
+
+/*
+ * This describes the I/O queue.
+ */
+struct pj_ioqueue_t
+{
+ pj_lock_t *lock;
+ pj_bool_t auto_delete_lock;
+ unsigned max, count;
+ pj_ioqueue_key_t hlist;
+ int epfd;
+};
+
+/*
+ * pj_ioqueue_create()
+ *
+ * Create select ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
+ pj_size_t max_fd,
+ int max_threads,
+ pj_ioqueue_t **p_ioqueue)
+{
+ pj_ioqueue_t *ioque;
+ pj_status_t rc;
+
+ PJ_UNUSED_ARG(max_threads);
+
+ if (max_fd > PJ_IOQUEUE_MAX_HANDLES) {
+ pj_assert(!"max_fd too large");
+ return PJ_EINVAL;
+ }
+
+ ioque = pj_pool_alloc(pool, sizeof(pj_ioqueue_t));
+ ioque->max = max_fd;
+ ioque->count = 0;
+ pj_list_init(&ioque->hlist);
+
+ rc = pj_lock_create_recursive_mutex(pool, "ioq%p", &ioque->lock);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ ioque->auto_delete_lock = PJ_TRUE;
+ ioque->epfd = os_epoll_create(max_fd);
+ if (ioque->epfd < 0) {
+ return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+ }
+
+ PJ_LOG(4, ("pjlib", "select() I/O Queue created (%p)", ioque));
+
+ *p_ioqueue = ioque;
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_destroy()
+ *
+ * Destroy ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque)
+{
+ PJ_ASSERT_RETURN(ioque, PJ_EINVAL);
+ PJ_ASSERT_RETURN(ioque->epfd > 0, PJ_EINVALIDOP);
+
+ pj_lock_acquire(ioque->lock);
+ os_close(ioque->epfd);
+ ioque->epfd = 0;
+ if (ioque->auto_delete_lock)
+ pj_lock_destroy(ioque->lock);
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_set_lock()
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque,
+ pj_lock_t *lock,
+ pj_bool_t auto_delete )
+{
+ PJ_ASSERT_RETURN(ioque && lock, PJ_EINVAL);
+
+ if (ioque->auto_delete_lock) {
+ pj_lock_destroy(ioque->lock);
+ }
+
+ ioque->lock = lock;
+ ioque->auto_delete_lock = auto_delete;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * pj_ioqueue_register_sock()
+ *
+ * Register a socket to ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
+ pj_ioqueue_t *ioque,
+ pj_sock_t sock,
+ void *user_data,
+ const pj_ioqueue_callback *cb,
+ pj_ioqueue_key_t **p_key)
+{
+ pj_ioqueue_key_t *key = NULL;
+ pj_uint32_t value;
+ struct epoll_event ev;
+ int status;
+ pj_status_t rc = PJ_SUCCESS;
+
+ PJ_ASSERT_RETURN(pool && ioque && sock != PJ_INVALID_SOCKET &&
+ cb && p_key, PJ_EINVAL);
+
+ pj_lock_acquire(ioque->lock);
+
+ if (ioque->count >= ioque->max) {
+ rc = PJ_ETOOMANY;
+ TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: too many files"));
+ goto on_return;
+ }
+
+ /* Set socket to nonblocking. */
+ value = 1;
+ if ((rc=os_ioctl(sock, FIONBIO, (ioctl_val_type)&value))) {
+ TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: ioctl rc=%d",
+ rc));
+ rc = pj_get_netos_error();
+ goto on_return;
+ }
+
+ /* Create key. */
+ key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t));
+ key->fd = sock;
+ key->user_data = user_data;
+ pj_memcpy(&key->cb, cb, sizeof(pj_ioqueue_callback));
+
+ /* os_epoll_ctl. */
+ ev.events = EPOLLIN | EPOLLOUT | EPOLLERR;
+ ev.epoll_data = (epoll_data_type)key;
+ status = os_epoll_ctl(ioque->epfd, EPOLL_CTL_ADD, sock, &ev);
+ if (status < 0) {
+ rc = pj_get_os_error();
+ TRACE_((THIS_FILE,
+ "pj_ioqueue_register_sock error: os_epoll_ctl rc=%d",
+ status));
+ goto on_return;
+ }
+
+ /* Register */
+ pj_list_insert_before(&ioque->hlist, key);
+ ++ioque->count;
+
+on_return:
+ *p_key = key;
+ pj_lock_release(ioque->lock);
+
+ return rc;
+}
+
+/*
+ * pj_ioqueue_unregister()
+ *
+ * Unregister handle from ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key)
+{
+ struct epoll_event ev;
+ int status;
+
+ PJ_ASSERT_RETURN(ioque && key, PJ_EINVAL);
+
+ pj_lock_acquire(ioque->lock);
+
+ pj_assert(ioque->count > 0);
+ --ioque->count;
+ pj_list_erase(key);
+
+ ev.events = 0;
+ ev.epoll_data = (epoll_data_type)key;
+ status = os_epoll_ctl( ioque->epfd, EPOLL_CTL_DEL, key->fd, &ev);
+ if (status != 0) {
+ pj_status_t rc = pj_get_os_error();
+ pj_lock_release(ioque->lock);
+ return rc;
+ }
+
+ pj_lock_release(ioque->lock);
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_get_user_data()
+ *
+ * Obtain value associated with a key.
+ */
+PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
+{
+ PJ_ASSERT_RETURN(key != NULL, NULL);
+ return key->user_data;
+}
+
+
+/*
+ * pj_ioqueue_poll()
+ *
+ */
+PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout)
+{
+ int i, count, processed;
+ struct epoll_event events[16];
+ int msec;
+
+ PJ_CHECK_STACK();
+
+ msec = timeout ? PJ_TIME_VAL_MSEC(*timeout) : 9000;
+
+ count = os_epoll_wait( ioque->epfd, events, PJ_ARRAY_SIZE(events), msec);
+ if (count <= 0)
+ return count;
+
+ /* Lock ioqueue. */
+ pj_lock_acquire(ioque->lock);
+
+ processed = 0;
+
+ for (i=0; i<count; ++i) {
+ pj_ioqueue_key_t *h = (pj_ioqueue_key_t*)(epoll_data_type)
+ events[i].epoll_data;
+ pj_status_t rc;
+
+ /*
+ * Check for completion of read operations.
+ */
+ if ((events[i].events & EPOLLIN) && (PJ_IOQUEUE_IS_READ_OP(h->op))) {
+ pj_ssize_t bytes_read = h->rd_buflen;
+
+ if ((h->op & PJ_IOQUEUE_OP_RECV_FROM)) {
+ rc = pj_sock_recvfrom( h->fd, h->rd_buf, &bytes_read, 0,
+ h->rmt_addr, h->rmt_addrlen);
+ } else if ((h->op & PJ_IOQUEUE_OP_RECV)) {
+ rc = pj_sock_recv(h->fd, h->rd_buf, &bytes_read, 0);
+ } else {
+ bytes_read = os_read( h->fd, h->rd_buf, bytes_read);
+ rc = (bytes_read >= 0) ? PJ_SUCCESS : pj_get_os_error();
+ }
+
+ if (rc != PJ_SUCCESS) {
+ bytes_read = -rc;
+ }
+
+ h->op &= ~(PJ_IOQUEUE_OP_READ | PJ_IOQUEUE_OP_RECV |
+ PJ_IOQUEUE_OP_RECV_FROM);
+
+ /* Call callback. */
+ (*h->cb.on_read_complete)(h, bytes_read);
+
+ ++processed;
+ }
+ /*
+ * Check for completion of accept() operation.
+ */
+ else if ((events[i].events & EPOLLIN) &&
+ (h->op & PJ_IOQUEUE_OP_ACCEPT))
+ {
+ /* accept() must be the only operation specified on
+ * server socket
+ */
+ pj_assert( h->op == PJ_IOQUEUE_OP_ACCEPT);
+
+ rc = pj_sock_accept( h->fd, h->accept_fd,
+ h->rmt_addr, h->rmt_addrlen);
+ if (rc==PJ_SUCCESS && h->local_addr) {
+ rc = pj_sock_getsockname(*h->accept_fd, h->local_addr,
+ h->local_addrlen);
+ }
+
+ h->op &= ~(PJ_IOQUEUE_OP_ACCEPT);
+
+ /* Call callback. */
+ (*h->cb.on_accept_complete)(h, *h->accept_fd, rc);
+
+ ++processed;
+ }
+
+ /*
+ * Check for completion of write operations.
+ */
+ if ((events[i].events & EPOLLOUT) && PJ_IOQUEUE_IS_WRITE_OP(h->op)) {
+ /* Completion of write(), send(), or sendto() operation. */
+
+ /* Clear operation. */
+ h->op &= ~(PJ_IOQUEUE_OP_WRITE | PJ_IOQUEUE_OP_SEND |
+ PJ_IOQUEUE_OP_SEND_TO);
+
+ /* Call callback. */
+ /* All data must have been sent? */
+ (*h->cb.on_write_complete)(h, h->wr_buflen);
+
+ ++processed;
+ }
+#if PJ_HAS_TCP
+ /*
+ * Check for completion of connect() operation.
+ */
+ else if ((events[i].events & EPOLLOUT) &&
+ (h->op & PJ_IOQUEUE_OP_CONNECT))
+ {
+ /* Completion of connect() operation */
+ pj_ssize_t bytes_transfered;
+
+ /* from connect(2):
+ * On Linux, use getsockopt to read the SO_ERROR option at
+ * level SOL_SOCKET to determine whether connect() completed
+ * successfully (if SO_ERROR is zero).
+ */
+ int value;
+ socklen_t vallen = sizeof(value);
+ int gs_rc = os_getsockopt(h->fd, SOL_SOCKET, SO_ERROR,
+ (getsockopt_val_ptr)&value, &vallen);
+ if (gs_rc != 0) {
+ /* Argh!! What to do now???
+ * Just indicate that the socket is connected. The
+ * application will get error as soon as it tries to use
+ * the socket to send/receive.
+ */
+ bytes_transfered = 0;
+ } else {
+ bytes_transfered = value;
+ }
+
+ /* Clear operation. */
+ h->op &= (~PJ_IOQUEUE_OP_CONNECT);
+
+ /* Call callback. */
+ (*h->cb.on_connect_complete)(h, bytes_transfered);
+
+ ++processed;
+ }
+#endif /* PJ_HAS_TCP */
+
+ /*
+ * Check for error condition.
+ */
+ if (events[i].events & EPOLLERR) {
+ if (h->op & PJ_IOQUEUE_OP_CONNECT) {
+ h->op &= ~PJ_IOQUEUE_OP_CONNECT;
+
+ /* Call callback. */
+ (*h->cb.on_connect_complete)(h, -1);
+
+ ++processed;
+ }
+ }
+ }
+
+ pj_lock_release(ioque->lock);
+
+ return processed;
+}
+
+/*
+ * pj_ioqueue_read()
+ *
+ * Start asynchronous read from the descriptor.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen)
+{
+ PJ_ASSERT_RETURN(ioque && key && buffer, PJ_EINVAL);
+ PJ_CHECK_STACK();
+
+ /* For consistency with other ioqueue implementation, we would reject
+ * if descriptor has already been submitted for reading before.
+ */
+ PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_READ) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_RECV) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_RECV_FROM) == 0),
+ PJ_EBUSY);
+
+ pj_lock_acquire(ioque->lock);
+
+ key->op |= PJ_IOQUEUE_OP_READ;
+ key->rd_flags = 0;
+ key->rd_buf = buffer;
+ key->rd_buflen = buflen;
+
+ pj_lock_release(ioque->lock);
+ return PJ_EPENDING;
+}
+
+
+/*
+ * pj_ioqueue_recv()
+ *
+ * Start asynchronous recv() from the socket.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen,
+ unsigned flags )
+{
+ PJ_ASSERT_RETURN(ioque && key && buffer, PJ_EINVAL);
+ PJ_CHECK_STACK();
+
+ /* For consistency with other ioqueue implementation, we would reject
+ * if descriptor has already been submitted for reading before.
+ */
+ PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_READ) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_RECV) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_RECV_FROM) == 0),
+ PJ_EBUSY);
+
+ pj_lock_acquire(ioque->lock);
+
+ key->op |= PJ_IOQUEUE_OP_RECV;
+ key->rd_buf = buffer;
+ key->rd_buflen = buflen;
+ key->rd_flags = flags;
+
+ pj_lock_release(ioque->lock);
+ return PJ_EPENDING;
+}
+
+/*
+ * pj_ioqueue_recvfrom()
+ *
+ * Start asynchronous recvfrom() from the socket.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen,
+ unsigned flags,
+ pj_sockaddr_t *addr,
+ int *addrlen)
+{
+ PJ_ASSERT_RETURN(ioque && key && buffer, PJ_EINVAL);
+ PJ_CHECK_STACK();
+
+ /* For consistency with other ioqueue implementation, we would reject
+ * if descriptor has already been submitted for reading before.
+ */
+ PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_READ) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_RECV) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_RECV_FROM) == 0),
+ PJ_EBUSY);
+
+ pj_lock_acquire(ioque->lock);
+
+ key->op |= PJ_IOQUEUE_OP_RECV_FROM;
+ key->rd_buf = buffer;
+ key->rd_buflen = buflen;
+ key->rd_flags = flags;
+ key->rmt_addr = addr;
+ key->rmt_addrlen = addrlen;
+
+ pj_lock_release(ioque->lock);
+ return PJ_EPENDING;
+}
+
+/*
+ * pj_ioqueue_write()
+ *
+ * Start asynchronous write() to the descriptor.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen)
+{
+ pj_status_t rc;
+ pj_ssize_t sent;
+
+ PJ_ASSERT_RETURN(ioque && key && data, PJ_EINVAL);
+ PJ_CHECK_STACK();
+
+ /* For consistency with other ioqueue implementation, we would reject
+ * if descriptor has already been submitted for writing before.
+ */
+ PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_WRITE) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_SEND) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_SEND_TO) == 0),
+ PJ_EBUSY);
+
+ sent = datalen;
+ /* sent would be -1 after pj_sock_send() if it returns error. */
+ rc = pj_sock_send(key->fd, data, &sent, 0);
+ if (rc != PJ_SUCCESS && rc != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) {
+ return rc;
+ }
+
+ pj_lock_acquire(ioque->lock);
+
+ key->op |= PJ_IOQUEUE_OP_WRITE;
+ key->wr_buf = NULL;
+ key->wr_buflen = datalen;
+
+ pj_lock_release(ioque->lock);
+
+ return PJ_EPENDING;
+}
+
+/*
+ * pj_ioqueue_send()
+ *
+ * Start asynchronous send() to the descriptor.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen,
+ unsigned flags)
+{
+ pj_status_t rc;
+ pj_ssize_t sent;
+
+ PJ_ASSERT_RETURN(ioque && key && data, PJ_EINVAL);
+ PJ_CHECK_STACK();
+
+ /* For consistency with other ioqueue implementation, we would reject
+ * if descriptor has already been submitted for writing before.
+ */
+ PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_WRITE) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_SEND) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_SEND_TO) == 0),
+ PJ_EBUSY);
+
+ sent = datalen;
+ /* sent would be -1 after pj_sock_send() if it returns error. */
+ rc = pj_sock_send(key->fd, data, &sent, flags);
+ if (rc != PJ_SUCCESS && rc != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) {
+ return rc;
+ }
+
+ pj_lock_acquire(ioque->lock);
+
+ key->op |= PJ_IOQUEUE_OP_SEND;
+ key->wr_buf = NULL;
+ key->wr_buflen = datalen;
+
+ pj_lock_release(ioque->lock);
+
+ return PJ_EPENDING;
+}
+
+
+/*
+ * pj_ioqueue_sendto()
+ *
+ * Start asynchronous write() to the descriptor.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen,
+ unsigned flags,
+ const pj_sockaddr_t *addr,
+ int addrlen)
+{
+ pj_status_t rc;
+ pj_ssize_t sent;
+
+ PJ_ASSERT_RETURN(ioque && key && data, PJ_EINVAL);
+ PJ_CHECK_STACK();
+
+ /* For consistency with other ioqueue implementation, we would reject
+ * if descriptor has already been submitted for writing before.
+ */
+ PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_WRITE) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_SEND) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_SEND_TO) == 0),
+ PJ_EBUSY);
+
+ sent = datalen;
+ /* sent would be -1 after pj_sock_sendto() if it returns error. */
+ rc = pj_sock_sendto(key->fd, data, &sent, flags, addr, addrlen);
+ if (rc != PJ_SUCCESS && rc != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) {
+ return rc;
+ }
+
+ pj_lock_acquire(ioque->lock);
+
+ key->op |= PJ_IOQUEUE_OP_SEND_TO;
+ key->wr_buf = NULL;
+ key->wr_buflen = datalen;
+
+ pj_lock_release(ioque->lock);
+ return PJ_EPENDING;
+}
+
+#if PJ_HAS_TCP
+/*
+ * Initiate overlapped accept() operation.
+ */
+PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue,
+ pj_ioqueue_key_t *key,
+ pj_sock_t *new_sock,
+ pj_sockaddr_t *local,
+ pj_sockaddr_t *remote,
+ int *addrlen)
+{
+ /* check parameters. All must be specified! */
+ pj_assert(ioqueue && key && new_sock);
+
+ /* Server socket must have no other operation! */
+ pj_assert(key->op == 0);
+
+ pj_lock_acquire(ioqueue->lock);
+
+ key->op = PJ_IOQUEUE_OP_ACCEPT;
+ key->accept_fd = new_sock;
+ key->rmt_addr = remote;
+ key->rmt_addrlen = addrlen;
+ key->local_addr = local;
+ key->local_addrlen = addrlen; /* use same addr. as rmt_addrlen */
+
+ pj_lock_release(ioqueue->lock);
+ return PJ_EPENDING;
+}
+
+/*
+ * Initiate overlapped connect() operation (well, it's non-blocking actually,
+ * since there's no overlapped version of connect()).
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue,
+ pj_ioqueue_key_t *key,
+ const pj_sockaddr_t *addr,
+ int addrlen )
+{
+ pj_status_t rc;
+
+ /* check parameters. All must be specified! */
+ PJ_ASSERT_RETURN(ioqueue && key && addr && addrlen, PJ_EINVAL);
+
+ /* Connecting socket must have no other operation! */
+ PJ_ASSERT_RETURN(key->op == 0, PJ_EBUSY);
+
+ rc = pj_sock_connect(key->fd, addr, addrlen);
+ if (rc == PJ_SUCCESS) {
+ /* Connected! */
+ return PJ_SUCCESS;
+ } else {
+ if (rc == PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) ||
+ rc == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK))
+ {
+ /* Pending! */
+ pj_lock_acquire(ioqueue->lock);
+ key->op = PJ_IOQUEUE_OP_CONNECT;
+ pj_lock_release(ioqueue->lock);
+ return PJ_EPENDING;
+ } else {
+ /* Error! */
+ return rc;
+ }
+ }
+}
+#endif /* PJ_HAS_TCP */
+
diff --git a/pjlib/src/pj/ioqueue_linux_kernel.c b/pjlib/src/pj/ioqueue_linux_kernel.c
new file mode 100644
index 00000000..b8338118
--- /dev/null
+++ b/pjlib/src/pj/ioqueue_linux_kernel.c
@@ -0,0 +1,150 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/ioqueue_linux_kernel.c 1 10/05/05 4:42p Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/ioqueue_linux_kernel.c $
+ *
+ * 1 10/05/05 4:42p Bennylp
+ * Created.
+ *
+ */
+#include <pj/ioqueue.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/list.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/sock.h>
+
+#define THIS_FILE "ioqueue"
+
+#define PJ_IOQUEUE_IS_READ_OP(op) \
+ ((op & PJ_IOQUEUE_OP_READ) || (op & PJ_IOQUEUE_OP_RECV_FROM))
+#define PJ_IOQUEUE_IS_WRITE_OP(op) \
+ ((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO))
+
+
+#if PJ_HAS_TCP
+# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT)
+# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT)
+#else
+# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0
+# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0
+#endif
+
+#if defined(PJ_DEBUG) && PJ_DEBUG != 0
+# define VALIDATE_FD_SET 1
+#else
+# define VALIDATE_FD_SET 0
+#endif
+
+struct pj_ioqueue_key_t
+{
+ PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t)
+ pj_sock_t fd;
+ pj_ioqueue_operation_e op;
+ void *user_data;
+ pj_ioqueue_callback cb;
+};
+
+struct pj_ioqueue_t
+{
+};
+
+PJ_DEF(pj_ioqueue_t*) pj_ioqueue_create(pj_pool_t *pool, pj_size_t max_fd)
+{
+ return NULL;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque)
+{
+ return 0;
+}
+
+PJ_DEF(pj_ioqueue_key_t*) pj_ioqueue_register( pj_pool_t *pool,
+ pj_ioqueue_t *ioque,
+ pj_oshandle_t sock,
+ void *user_data,
+ const pj_ioqueue_callback *cb)
+{
+ return NULL;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key)
+{
+ return -1;
+}
+
+PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
+{
+ return NULL;
+}
+
+
+PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout)
+{
+ return -1;
+}
+
+PJ_DEF(int) pj_ioqueue_read( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen)
+{
+ return -1;
+}
+
+PJ_DEF(int) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen,
+ pj_sockaddr_t *addr,
+ int *addrlen)
+{
+ return -1;
+}
+
+PJ_DEF(int) pj_ioqueue_write( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen)
+{
+ return -1;
+}
+
+PJ_DEF(int) pj_ioqueue_sendto( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen,
+ const pj_sockaddr_t *addr,
+ int addrlen)
+{
+ return -1;
+}
+
+#if PJ_HAS_TCP
+/*
+ * Initiate overlapped accept() operation.
+ */
+PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue,
+ pj_ioqueue_key_t *key,
+ pj_sock_t *new_sock,
+ pj_sockaddr_t *local,
+ pj_sockaddr_t *remote,
+ int *addrlen)
+{
+ return -1;
+}
+
+/*
+ * Initiate overlapped connect() operation (well, it's non-blocking actually,
+ * since there's no overlapped version of connect()).
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue,
+ pj_ioqueue_key_t *key,
+ const pj_sockaddr_t *addr,
+ int addrlen )
+{
+ return -1;
+}
+#endif /* PJ_HAS_TCP */
+
diff --git a/pjlib/src/pj/ioqueue_select.c b/pjlib/src/pj/ioqueue_select.c
new file mode 100644
index 00000000..615c758e
--- /dev/null
+++ b/pjlib/src/pj/ioqueue_select.c
@@ -0,0 +1,947 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/ioqueue_select.c 15 10/29/05 10:27p Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/ioqueue_select.c $
+ *
+ * 15 10/29/05 10:27p Bennylp
+ * Fixed misc warnings.
+ *
+ * 14 10/29/05 11:31a Bennylp
+ * Changed accept and lock.
+ *
+ * 13 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 12 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 11 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+
+/*
+ * sock_select.c
+ *
+ * This is the implementation of IOQueue using pj_sock_select().
+ * It runs anywhere where pj_sock_select() is available (currently
+ * Win32, Linux, Linux kernel, etc.).
+ */
+
+#include <pj/ioqueue.h>
+#include <pj/os.h>
+#include <pj/lock.h>
+#include <pj/log.h>
+#include <pj/list.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/sock.h>
+#include <pj/compat/socket.h>
+#include <pj/sock_select.h>
+#include <pj/errno.h>
+
+/*
+ * ISSUES with ioqueue_select()
+ *
+ * EAGAIN/EWOULDBLOCK error in recv():
+ * - when multiple threads are working with the ioqueue, application
+ * may receive EAGAIN or EWOULDBLOCK in the receive callback.
+ * This error happens because more than one thread is watching for
+ * the same descriptor set, so when all of them call recv() or recvfrom()
+ * simultaneously, only one will succeed and the rest will get the error.
+ *
+ */
+#define THIS_FILE "ioq_select"
+
+#define PJ_IOQUEUE_IS_READ_OP(op) ((op & PJ_IOQUEUE_OP_READ) || \
+ (op & PJ_IOQUEUE_OP_RECV) || \
+ (op & PJ_IOQUEUE_OP_RECV_FROM))
+#define PJ_IOQUEUE_IS_WRITE_OP(op) ((op & PJ_IOQUEUE_OP_WRITE) || \
+ (op & PJ_IOQUEUE_OP_SEND) || \
+ (op & PJ_IOQUEUE_OP_SEND_TO))
+
+
+#if PJ_HAS_TCP
+# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT)
+# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT)
+#else
+# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0
+# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0
+#endif
+
+/*
+ * During debugging build, VALIDATE_FD_SET is set.
+ * This will check the validity of the fd_sets.
+ */
+#if defined(PJ_DEBUG) && PJ_DEBUG != 0
+# define VALIDATE_FD_SET 1
+#else
+# define VALIDATE_FD_SET 0
+#endif
+
+/*
+ * This describes each key.
+ */
+struct pj_ioqueue_key_t
+{
+ PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t)
+ pj_sock_t fd;
+ pj_ioqueue_operation_e op;
+ void *user_data;
+ pj_ioqueue_callback cb;
+
+ void *rd_buf;
+ unsigned rd_flags;
+ pj_size_t rd_buflen;
+ void *wr_buf;
+ pj_size_t wr_buflen;
+
+ pj_sockaddr_t *rmt_addr;
+ int *rmt_addrlen;
+
+ pj_sockaddr_t *local_addr;
+ int *local_addrlen;
+
+ pj_sock_t *accept_fd;
+};
+
+/*
+ * This describes the I/O queue itself.
+ */
+struct pj_ioqueue_t
+{
+ pj_lock_t *lock;
+ pj_bool_t auto_delete_lock;
+ unsigned max, count;
+ pj_ioqueue_key_t hlist;
+ pj_fd_set_t rfdset;
+ pj_fd_set_t wfdset;
+#if PJ_HAS_TCP
+ pj_fd_set_t xfdset;
+#endif
+};
+
+/*
+ * pj_ioqueue_create()
+ *
+ * Create select ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
+ pj_size_t max_fd,
+ int max_threads,
+ pj_ioqueue_t **p_ioqueue)
+{
+ pj_ioqueue_t *ioque;
+ pj_status_t rc;
+
+ PJ_UNUSED_ARG(max_threads);
+
+ if (max_fd > PJ_IOQUEUE_MAX_HANDLES) {
+ pj_assert(!"max_fd too large");
+ return PJ_EINVAL;
+ }
+
+ ioque = pj_pool_alloc(pool, sizeof(pj_ioqueue_t));
+ ioque->max = max_fd;
+ ioque->count = 0;
+ PJ_FD_ZERO(&ioque->rfdset);
+ PJ_FD_ZERO(&ioque->wfdset);
+#if PJ_HAS_TCP
+ PJ_FD_ZERO(&ioque->xfdset);
+#endif
+ pj_list_init(&ioque->hlist);
+
+ rc = pj_lock_create_recursive_mutex(pool, "ioq%p", &ioque->lock);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ ioque->auto_delete_lock = PJ_TRUE;
+
+ PJ_LOG(4, ("pjlib", "select() I/O Queue created (%p)", ioque));
+
+ *p_ioqueue = ioque;
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_destroy()
+ *
+ * Destroy ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque)
+{
+ pj_status_t rc = PJ_SUCCESS;
+
+ PJ_ASSERT_RETURN(ioque, PJ_EINVAL);
+
+ if (ioque->auto_delete_lock)
+ rc = pj_lock_destroy(ioque->lock);
+
+ return rc;
+}
+
+
+/*
+ * pj_ioqueue_set_lock()
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque,
+ pj_lock_t *lock,
+ pj_bool_t auto_delete )
+{
+ PJ_ASSERT_RETURN(ioque && lock, PJ_EINVAL);
+
+ if (ioque->auto_delete_lock) {
+ pj_lock_destroy(ioque->lock);
+ }
+
+ ioque->lock = lock;
+ ioque->auto_delete_lock = auto_delete;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * pj_ioqueue_register_sock()
+ *
+ * Register a handle to ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
+ pj_ioqueue_t *ioque,
+ pj_sock_t sock,
+ void *user_data,
+ const pj_ioqueue_callback *cb,
+ pj_ioqueue_key_t **p_key)
+{
+ pj_ioqueue_key_t *key = NULL;
+ pj_uint32_t value;
+ pj_status_t rc = PJ_SUCCESS;
+
+ PJ_ASSERT_RETURN(pool && ioque && sock != PJ_INVALID_SOCKET &&
+ cb && p_key, PJ_EINVAL);
+
+ pj_lock_acquire(ioque->lock);
+
+ if (ioque->count >= ioque->max) {
+ rc = PJ_ETOOMANY;
+ goto on_return;
+ }
+
+ /* Set socket to nonblocking. */
+ value = 1;
+#ifdef PJ_WIN32
+ if (ioctlsocket(sock, FIONBIO, (unsigned long*)&value)) {
+#else
+ if (ioctl(sock, FIONBIO, &value)) {
+#endif
+ rc = pj_get_netos_error();
+ goto on_return;
+ }
+
+ /* Create key. */
+ key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t));
+ key->fd = sock;
+ key->user_data = user_data;
+
+ /* Save callback. */
+ pj_memcpy(&key->cb, cb, sizeof(pj_ioqueue_callback));
+
+ /* Register */
+ pj_list_insert_before(&ioque->hlist, key);
+ ++ioque->count;
+
+on_return:
+ *p_key = key;
+ pj_lock_release(ioque->lock);
+
+ return rc;
+}
+
+/*
+ * pj_ioqueue_unregister()
+ *
+ * Unregister handle from ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key)
+{
+ PJ_ASSERT_RETURN(ioque && key, PJ_EINVAL);
+
+ pj_lock_acquire(ioque->lock);
+
+ pj_assert(ioque->count > 0);
+ --ioque->count;
+ pj_list_erase(key);
+ PJ_FD_CLR(key->fd, &ioque->rfdset);
+ PJ_FD_CLR(key->fd, &ioque->wfdset);
+#if PJ_HAS_TCP
+ PJ_FD_CLR(key->fd, &ioque->xfdset);
+#endif
+
+ pj_lock_release(ioque->lock);
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_get_user_data()
+ *
+ * Obtain value associated with a key.
+ */
+PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
+{
+ PJ_ASSERT_RETURN(key != NULL, NULL);
+ return key->user_data;
+}
+
+
+/* This supposed to check whether the fd_set values are consistent
+ * with the operation currently set in each key.
+ */
+#if VALIDATE_FD_SET
+static void validate_sets(const pj_ioqueue_t *ioque,
+ const pj_fd_set_t *rfdset,
+ const pj_fd_set_t *wfdset,
+ const pj_fd_set_t *xfdset)
+{
+ pj_ioqueue_key_t *key;
+
+ key = ioque->hlist.next;
+ while (key != &ioque->hlist) {
+ if ((key->op & PJ_IOQUEUE_OP_READ)
+ || (key->op & PJ_IOQUEUE_OP_RECV)
+ || (key->op & PJ_IOQUEUE_OP_RECV_FROM)
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
+ || (key->op & PJ_IOQUEUE_OP_ACCEPT)
+#endif
+ )
+ {
+ pj_assert(PJ_FD_ISSET(key->fd, rfdset));
+ }
+ else {
+ pj_assert(PJ_FD_ISSET(key->fd, rfdset) == 0);
+ }
+ if ((key->op & PJ_IOQUEUE_OP_WRITE)
+ || (key->op & PJ_IOQUEUE_OP_SEND)
+ || (key->op & PJ_IOQUEUE_OP_SEND_TO)
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
+ || (key->op & PJ_IOQUEUE_OP_CONNECT)
+#endif
+ )
+ {
+ pj_assert(PJ_FD_ISSET(key->fd, wfdset));
+ }
+ else {
+ pj_assert(PJ_FD_ISSET(key->fd, wfdset) == 0);
+ }
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
+ if (key->op & PJ_IOQUEUE_OP_CONNECT)
+ {
+ pj_assert(PJ_FD_ISSET(key->fd, xfdset));
+ }
+ else {
+ pj_assert(PJ_FD_ISSET(key->fd, xfdset) == 0);
+ }
+#endif /* PJ_HAS_TCP */
+
+ key = key->next;
+ }
+}
+#endif /* VALIDATE_FD_SET */
+
+
+/*
+ * pj_ioqueue_poll()
+ *
+ * Few things worth written:
+ *
+ * - we used to do only one callback called per poll, but it didn't go
+ * very well. The reason is because on some situation, the write
+ * callback gets called all the time, thus doesn't give the read
+ * callback to get called. This happens, for example, when user
+ * submit write operation inside the write callback.
+ * As the result, we changed the behaviour so that now multiple
+ * callbacks are called in a single poll. It should be fast too,
+ * just that we need to be carefull with the ioqueue data structs.
+ *
+ * - to guarantee preemptiveness etc, the poll function must strictly
+ * work on fd_set copy of the ioqueue (not the original one).
+ */
+PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout)
+{
+ pj_fd_set_t rfdset, wfdset, xfdset;
+ int count;
+ pj_ioqueue_key_t *h;
+
+ /* Lock ioqueue before making fd_set copies */
+ pj_lock_acquire(ioque->lock);
+
+ if (PJ_FD_COUNT(&ioque->rfdset)==0 &&
+ PJ_FD_COUNT(&ioque->wfdset)==0 &&
+ PJ_FD_COUNT(&ioque->xfdset)==0)
+ {
+ pj_lock_release(ioque->lock);
+ if (timeout)
+ pj_thread_sleep(PJ_TIME_VAL_MSEC(*timeout));
+ return 0;
+ }
+
+ /* Copy ioqueue's pj_fd_set_t to local variables. */
+ pj_memcpy(&rfdset, &ioque->rfdset, sizeof(pj_fd_set_t));
+ pj_memcpy(&wfdset, &ioque->wfdset, sizeof(pj_fd_set_t));
+#if PJ_HAS_TCP
+ pj_memcpy(&xfdset, &ioque->xfdset, sizeof(pj_fd_set_t));
+#else
+ PJ_FD_ZERO(&xfdset);
+#endif
+
+#if VALIDATE_FD_SET
+ validate_sets(ioque, &rfdset, &wfdset, &xfdset);
+#endif
+
+ /* Unlock ioqueue before select(). */
+ pj_lock_release(ioque->lock);
+
+ count = pj_sock_select(FD_SETSIZE, &rfdset, &wfdset, &xfdset, timeout);
+
+ if (count <= 0)
+ return count;
+
+ /* Lock ioqueue again before scanning for signalled sockets. */
+ pj_lock_acquire(ioque->lock);
+
+#if PJ_HAS_TCP
+ /* Scan for exception socket */
+ h = ioque->hlist.next;
+do_except_scan:
+ for ( ; h!=&ioque->hlist; h = h->next) {
+ if ((h->op & PJ_IOQUEUE_OP_CONNECT) && PJ_FD_ISSET(h->fd, &xfdset))
+ break;
+ }
+ if (h != &ioque->hlist) {
+ /* 'connect()' should be the only operation. */
+ pj_assert((h->op == PJ_IOQUEUE_OP_CONNECT));
+
+ /* Clear operation. */
+ h->op &= ~(PJ_IOQUEUE_OP_CONNECT);
+ PJ_FD_CLR(h->fd, &ioque->wfdset);
+ PJ_FD_CLR(h->fd, &ioque->xfdset);
+ PJ_FD_CLR(h->fd, &wfdset);
+ PJ_FD_CLR(h->fd, &xfdset);
+
+ /* Call callback. */
+ if (h->cb.on_connect_complete)
+ (*h->cb.on_connect_complete)(h, -1);
+
+ /* Re-scan exception list. */
+ goto do_except_scan;
+ }
+#endif /* PJ_HAS_TCP */
+
+ /* Scan for readable socket. */
+ h = ioque->hlist.next;
+do_readable_scan:
+ for ( ; h!=&ioque->hlist; h = h->next) {
+ if ((PJ_IOQUEUE_IS_READ_OP(h->op) || PJ_IOQUEUE_IS_ACCEPT_OP(h->op)) &&
+ PJ_FD_ISSET(h->fd, &rfdset))
+ {
+ break;
+ }
+ }
+ if (h != &ioque->hlist) {
+ pj_status_t rc;
+
+ pj_assert(PJ_IOQUEUE_IS_READ_OP(h->op) ||
+ PJ_IOQUEUE_IS_ACCEPT_OP(h->op));
+
+# if PJ_HAS_TCP
+ if ((h->op & PJ_IOQUEUE_OP_ACCEPT)) {
+ /* accept() must be the only operation specified on server socket */
+ pj_assert(h->op == PJ_IOQUEUE_OP_ACCEPT);
+
+ rc=pj_sock_accept(h->fd, h->accept_fd, h->rmt_addr, h->rmt_addrlen);
+ if (rc==0 && h->local_addr) {
+ rc = pj_sock_getsockname(*h->accept_fd, h->local_addr,
+ h->local_addrlen);
+ }
+
+ h->op &= ~(PJ_IOQUEUE_OP_ACCEPT);
+ PJ_FD_CLR(h->fd, &ioque->rfdset);
+
+ /* Call callback. */
+ if (h->cb.on_accept_complete)
+ (*h->cb.on_accept_complete)(h, *h->accept_fd, rc);
+
+ /* Re-scan readable sockets. */
+ goto do_readable_scan;
+ }
+ else {
+# endif
+ pj_ssize_t bytes_read = h->rd_buflen;
+
+ if ((h->op & PJ_IOQUEUE_OP_RECV_FROM)) {
+ rc = pj_sock_recvfrom(h->fd, h->rd_buf, &bytes_read, 0,
+ h->rmt_addr, h->rmt_addrlen);
+ } else if ((h->op & PJ_IOQUEUE_OP_RECV)) {
+ rc = pj_sock_recv(h->fd, h->rd_buf, &bytes_read, 0);
+ } else {
+ /*
+ * User has specified pj_ioqueue_read().
+ * On Win32, we should do ReadFile(). But because we got
+ * here because of select() anyway, user must have put a
+ * socket descriptor on h->fd, which in this case we can
+ * just call pj_sock_recv() instead of ReadFile().
+ * On Unix, user may put a file in h->fd, so we'll have
+ * to call read() here.
+ * This may not compile on systems which doesn't have
+ * read(). That's why we only specify PJ_LINUX here so
+ * that error is easier to catch.
+ */
+# if defined(PJ_WIN32) && PJ_WIN32 != 0
+ rc = pj_sock_recv(h->fd, h->rd_buf, &bytes_read, 0);
+# elif defined(PJ_LINUX) && PJ_LINUX != 0
+ bytes_read = read(h->fd, h->rd_buf, bytes_read);
+ rc = (bytes_read >= 0) ? PJ_SUCCESS : pj_get_os_error();
+# elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0
+ bytes_read = sys_read(h->fd, h->rd_buf, bytes_read);
+ rc = (bytes_read >= 0) ? PJ_SUCCESS : -bytes_read;
+# else
+# error "Check this man!"
+# endif
+ }
+
+ if (rc != PJ_SUCCESS) {
+# if defined(PJ_WIN32) && PJ_WIN32 != 0
+ /* On Win32, for UDP, WSAECONNRESET on the receive side
+ * indicates that previous sending has triggered ICMP Port
+ * Unreachable message.
+ * But we wouldn't know at this point which one of previous
+ * key that has triggered the error, since UDP socket can
+ * be shared!
+ * So we'll just ignore it!
+ */
+
+ if (rc == PJ_STATUS_FROM_OS(WSAECONNRESET)) {
+ PJ_LOG(4,(THIS_FILE,
+ "Ignored ICMP port unreach. on key=%p", h));
+ }
+# endif
+
+ /* In any case we would report this to caller. */
+ bytes_read = -rc;
+ }
+
+ h->op &= ~(PJ_IOQUEUE_OP_READ | PJ_IOQUEUE_OP_RECV |
+ PJ_IOQUEUE_OP_RECV_FROM);
+ PJ_FD_CLR(h->fd, &ioque->rfdset);
+ PJ_FD_CLR(h->fd, &rfdset);
+
+ /* Call callback. */
+ if (h->cb.on_read_complete)
+ (*h->cb.on_read_complete)(h, bytes_read);
+
+ /* Re-scan readable sockets. */
+ goto do_readable_scan;
+
+ }
+ }
+
+ /* Scan for writable socket */
+ h = ioque->hlist.next;
+do_writable_scan:
+ for ( ; h!=&ioque->hlist; h = h->next) {
+ if ((PJ_IOQUEUE_IS_WRITE_OP(h->op) || PJ_IOQUEUE_IS_CONNECT_OP(h->op))
+ && PJ_FD_ISSET(h->fd, &wfdset))
+ {
+ break;
+ }
+ }
+ if (h != &ioque->hlist) {
+ pj_assert(PJ_IOQUEUE_IS_WRITE_OP(h->op) ||
+ PJ_IOQUEUE_IS_CONNECT_OP(h->op));
+
+#if PJ_HAS_TCP
+ if ((h->op & PJ_IOQUEUE_OP_CONNECT)) {
+ /* Completion of connect() operation */
+ pj_ssize_t bytes_transfered;
+
+#if defined(PJ_LINUX) || defined(PJ_LINUX_KERNEL)
+ /* from connect(2):
+ * On Linux, use getsockopt to read the SO_ERROR option at
+ * level SOL_SOCKET to determine whether connect() completed
+ * successfully (if SO_ERROR is zero).
+ */
+ int value;
+ socklen_t vallen = sizeof(value);
+ int gs_rc = getsockopt(h->fd, SOL_SOCKET, SO_ERROR,
+ &value, &vallen);
+ if (gs_rc != 0) {
+ /* Argh!! What to do now???
+ * Just indicate that the socket is connected. The
+ * application will get error as soon as it tries to use
+ * the socket to send/receive.
+ */
+ bytes_transfered = 0;
+ } else {
+ bytes_transfered = value;
+ }
+#elif defined(PJ_WIN32)
+ bytes_transfered = 0; /* success */
+#else
+# error "Got to check this one!"
+#endif
+
+ /* Clear operation. */
+ h->op &= (~PJ_IOQUEUE_OP_CONNECT);
+ PJ_FD_CLR(h->fd, &ioque->wfdset);
+ PJ_FD_CLR(h->fd, &ioque->xfdset);
+
+ /* Call callback. */
+ if (h->cb.on_connect_complete)
+ (*h->cb.on_connect_complete)(h, bytes_transfered);
+
+ /* Re-scan writable sockets. */
+ goto do_writable_scan;
+
+ } else
+#endif /* PJ_HAS_TCP */
+ {
+ /* Completion of write(), send(), or sendto() operation. */
+
+ /* Clear operation. */
+ h->op &= ~(PJ_IOQUEUE_OP_WRITE | PJ_IOQUEUE_OP_SEND |
+ PJ_IOQUEUE_OP_SEND_TO);
+ PJ_FD_CLR(h->fd, &ioque->wfdset);
+ PJ_FD_CLR(h->fd, &wfdset);
+
+ /* Call callback. */
+ /* All data must have been sent? */
+ if (h->cb.on_write_complete)
+ (*h->cb.on_write_complete)(h, h->wr_buflen);
+
+ /* Re-scan writable sockets. */
+ goto do_writable_scan;
+ }
+ }
+
+ /* Shouldn't happen. */
+ /* For strange reason on WinXP select() can return 1 while there is no
+ * pj_fd_set_t signaled. */
+ /* pj_assert(0); */
+
+ //count = 0;
+
+ pj_lock_release(ioque->lock);
+ return count;
+}
+
+/*
+ * pj_ioqueue_read()
+ *
+ * Start asynchronous read from the descriptor.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen)
+{
+ PJ_ASSERT_RETURN(ioque && key && buffer, PJ_EINVAL);
+ PJ_CHECK_STACK();
+
+ /* For consistency with other ioqueue implementation, we would reject
+ * if descriptor has already been submitted for reading before.
+ */
+ PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_READ) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_RECV) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_RECV_FROM) == 0),
+ PJ_EBUSY);
+
+ pj_lock_acquire(ioque->lock);
+
+ key->op |= PJ_IOQUEUE_OP_READ;
+ key->rd_flags = 0;
+ key->rd_buf = buffer;
+ key->rd_buflen = buflen;
+ PJ_FD_SET(key->fd, &ioque->rfdset);
+
+ pj_lock_release(ioque->lock);
+ return PJ_EPENDING;
+}
+
+
+/*
+ * pj_ioqueue_recv()
+ *
+ * Start asynchronous recv() from the socket.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen,
+ unsigned flags )
+{
+ PJ_ASSERT_RETURN(ioque && key && buffer, PJ_EINVAL);
+ PJ_CHECK_STACK();
+
+ /* For consistency with other ioqueue implementation, we would reject
+ * if descriptor has already been submitted for reading before.
+ */
+ PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_READ) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_RECV) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_RECV_FROM) == 0),
+ PJ_EBUSY);
+
+ pj_lock_acquire(ioque->lock);
+
+ key->op |= PJ_IOQUEUE_OP_RECV;
+ key->rd_buf = buffer;
+ key->rd_buflen = buflen;
+ key->rd_flags = flags;
+ PJ_FD_SET(key->fd, &ioque->rfdset);
+
+ pj_lock_release(ioque->lock);
+ return PJ_EPENDING;
+}
+
+/*
+ * pj_ioqueue_recvfrom()
+ *
+ * Start asynchronous recvfrom() from the socket.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen,
+ unsigned flags,
+ pj_sockaddr_t *addr,
+ int *addrlen)
+{
+ PJ_ASSERT_RETURN(ioque && key && buffer, PJ_EINVAL);
+ PJ_CHECK_STACK();
+
+ /* For consistency with other ioqueue implementation, we would reject
+ * if descriptor has already been submitted for reading before.
+ */
+ PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_READ) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_RECV) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_RECV_FROM) == 0),
+ PJ_EBUSY);
+
+ pj_lock_acquire(ioque->lock);
+
+ key->op |= PJ_IOQUEUE_OP_RECV_FROM;
+ key->rd_buf = buffer;
+ key->rd_buflen = buflen;
+ key->rd_flags = flags;
+ key->rmt_addr = addr;
+ key->rmt_addrlen = addrlen;
+ PJ_FD_SET(key->fd, &ioque->rfdset);
+
+ pj_lock_release(ioque->lock);
+ return PJ_EPENDING;
+}
+
+/*
+ * pj_ioqueue_write()
+ *
+ * Start asynchronous write() to the descriptor.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen)
+{
+ pj_status_t rc;
+ pj_ssize_t sent;
+
+ PJ_ASSERT_RETURN(ioque && key && data, PJ_EINVAL);
+ PJ_CHECK_STACK();
+
+ /* For consistency with other ioqueue implementation, we would reject
+ * if descriptor has already been submitted for writing before.
+ */
+ PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_WRITE) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_SEND) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_SEND_TO) == 0),
+ PJ_EBUSY);
+
+ sent = datalen;
+ /* sent would be -1 after pj_sock_send() if it returns error. */
+ rc = pj_sock_send(key->fd, data, &sent, 0);
+ if (rc != PJ_SUCCESS && rc != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) {
+ return rc;
+ }
+
+ pj_lock_acquire(ioque->lock);
+
+ key->op |= PJ_IOQUEUE_OP_WRITE;
+ key->wr_buf = NULL;
+ key->wr_buflen = datalen;
+ PJ_FD_SET(key->fd, &ioque->wfdset);
+
+ pj_lock_release(ioque->lock);
+
+ return PJ_EPENDING;
+}
+
+/*
+ * pj_ioqueue_send()
+ *
+ * Start asynchronous send() to the descriptor.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen,
+ unsigned flags)
+{
+ pj_status_t rc;
+ pj_ssize_t sent;
+
+ PJ_ASSERT_RETURN(ioque && key && data, PJ_EINVAL);
+ PJ_CHECK_STACK();
+
+ /* For consistency with other ioqueue implementation, we would reject
+ * if descriptor has already been submitted for writing before.
+ */
+ PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_WRITE) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_SEND) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_SEND_TO) == 0),
+ PJ_EBUSY);
+
+ sent = datalen;
+ /* sent would be -1 after pj_sock_send() if it returns error. */
+ rc = pj_sock_send(key->fd, data, &sent, flags);
+ if (rc != PJ_SUCCESS && rc != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) {
+ return rc;
+ }
+
+ pj_lock_acquire(ioque->lock);
+
+ key->op |= PJ_IOQUEUE_OP_SEND;
+ key->wr_buf = NULL;
+ key->wr_buflen = datalen;
+ PJ_FD_SET(key->fd, &ioque->wfdset);
+
+ pj_lock_release(ioque->lock);
+
+ return PJ_EPENDING;
+}
+
+
+/*
+ * pj_ioqueue_sendto()
+ *
+ * Start asynchronous write() to the descriptor.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen,
+ unsigned flags,
+ const pj_sockaddr_t *addr,
+ int addrlen)
+{
+ pj_status_t rc;
+ pj_ssize_t sent;
+
+ PJ_ASSERT_RETURN(ioque && key && data, PJ_EINVAL);
+ PJ_CHECK_STACK();
+
+ /* For consistency with other ioqueue implementation, we would reject
+ * if descriptor has already been submitted for writing before.
+ */
+ PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_WRITE) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_SEND) == 0 &&
+ (key->op & PJ_IOQUEUE_OP_SEND_TO) == 0),
+ PJ_EBUSY);
+
+ sent = datalen;
+ /* sent would be -1 after pj_sock_sendto() if it returns error. */
+ rc = pj_sock_sendto(key->fd, data, &sent, flags, addr, addrlen);
+ if (rc != PJ_SUCCESS && rc != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) {
+ return rc;
+ }
+
+ pj_lock_acquire(ioque->lock);
+
+ key->op |= PJ_IOQUEUE_OP_SEND_TO;
+ key->wr_buf = NULL;
+ key->wr_buflen = datalen;
+ PJ_FD_SET(key->fd, &ioque->wfdset);
+
+ pj_lock_release(ioque->lock);
+ return PJ_EPENDING;
+}
+
+#if PJ_HAS_TCP
+/*
+ * Initiate overlapped accept() operation.
+ */
+PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue,
+ pj_ioqueue_key_t *key,
+ pj_sock_t *new_sock,
+ pj_sockaddr_t *local,
+ pj_sockaddr_t *remote,
+ int *addrlen)
+{
+ /* check parameters. All must be specified! */
+ pj_assert(ioqueue && key && new_sock);
+
+ /* Server socket must have no other operation! */
+ pj_assert(key->op == 0);
+
+ pj_lock_acquire(ioqueue->lock);
+
+ key->op = PJ_IOQUEUE_OP_ACCEPT;
+ key->accept_fd = new_sock;
+ key->rmt_addr = remote;
+ key->rmt_addrlen = addrlen;
+ key->local_addr = local;
+ key->local_addrlen = addrlen; /* use same addr. as rmt_addrlen */
+
+ PJ_FD_SET(key->fd, &ioqueue->rfdset);
+
+ pj_lock_release(ioqueue->lock);
+ return PJ_EPENDING;
+}
+
+/*
+ * Initiate overlapped connect() operation (well, it's non-blocking actually,
+ * since there's no overlapped version of connect()).
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue,
+ pj_ioqueue_key_t *key,
+ const pj_sockaddr_t *addr,
+ int addrlen )
+{
+ pj_status_t rc;
+
+ /* check parameters. All must be specified! */
+ PJ_ASSERT_RETURN(ioqueue && key && addr && addrlen, PJ_EINVAL);
+
+ /* Connecting socket must have no other operation! */
+ PJ_ASSERT_RETURN(key->op == 0, PJ_EBUSY);
+
+ rc = pj_sock_connect(key->fd, addr, addrlen);
+ if (rc == PJ_SUCCESS) {
+ /* Connected! */
+ return PJ_SUCCESS;
+ } else {
+ if (rc == PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) ||
+ rc == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK))
+ {
+ /* Pending! */
+ pj_lock_acquire(ioqueue->lock);
+ key->op = PJ_IOQUEUE_OP_CONNECT;
+ PJ_FD_SET(key->fd, &ioqueue->wfdset);
+ PJ_FD_SET(key->fd, &ioqueue->xfdset);
+ pj_lock_release(ioqueue->lock);
+ return PJ_EPENDING;
+ } else {
+ /* Error! */
+ return rc;
+ }
+ }
+}
+#endif /* PJ_HAS_TCP */
+
diff --git a/pjlib/src/pj/ioqueue_winnt.c b/pjlib/src/pj/ioqueue_winnt.c
new file mode 100644
index 00000000..93116c9d
--- /dev/null
+++ b/pjlib/src/pj/ioqueue_winnt.c
@@ -0,0 +1,852 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/ioqueue_winnt.c 11 10/29/05 11:31a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/ioqueue_winnt.c $
+ *
+ * 11 10/29/05 11:31a Bennylp
+ * Changed accept and lock.
+ *
+ * 10 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 9 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/ioqueue.h>
+#include <pj/os.h>
+#include <pj/lock.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/sock.h>
+#include <pj/array.h>
+#include <pj/log.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+
+
+#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0
+# include <winsock2.h>
+#elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0
+# include <winsock.h>
+#endif
+
+#if defined(PJ_HAS_MSWSOCK_H) && PJ_HAS_MSWSOCK_H != 0
+# include <mswsock.h>
+#endif
+
+
+#define ACCEPT_ADDR_LEN (sizeof(pj_sockaddr_in)+20)
+
+/*
+ * OVERLAP structure for send and receive.
+ */
+typedef struct ioqueue_overlapped
+{
+ WSAOVERLAPPED overlapped;
+ pj_ioqueue_operation_e operation;
+ WSABUF wsabuf;
+} ioqueue_overlapped;
+
+#if PJ_HAS_TCP
+/*
+ * OVERLAP structure for accept.
+ */
+typedef struct ioqueue_accept_rec
+{
+ WSAOVERLAPPED overlapped;
+ pj_ioqueue_operation_e operation;
+ pj_sock_t newsock;
+ pj_sock_t *newsock_ptr;
+ int *addrlen;
+ void *remote;
+ void *local;
+ char accept_buf[2 * ACCEPT_ADDR_LEN];
+} ioqueue_accept_rec;
+#endif
+
+/*
+ * Structure for individual socket.
+ */
+struct pj_ioqueue_key_t
+{
+ HANDLE hnd;
+ void *user_data;
+ ioqueue_overlapped recv_overlapped;
+ ioqueue_overlapped send_overlapped;
+#if PJ_HAS_TCP
+ int connecting;
+ ioqueue_accept_rec accept_overlapped;
+#endif
+ pj_ioqueue_callback cb;
+};
+
+/*
+ * IO Queue structure.
+ */
+struct pj_ioqueue_t
+{
+ HANDLE iocp;
+ pj_lock_t *lock;
+ pj_bool_t auto_delete_lock;
+ unsigned event_count;
+ HANDLE event_pool[MAXIMUM_WAIT_OBJECTS+1];
+#if PJ_HAS_TCP
+ unsigned connecting_count;
+ HANDLE connecting_handles[MAXIMUM_WAIT_OBJECTS+1];
+ pj_ioqueue_key_t *connecting_keys[MAXIMUM_WAIT_OBJECTS+1];
+#endif
+};
+
+
+#if PJ_HAS_TCP
+/*
+ * Process the socket when the overlapped accept() completed.
+ */
+static void ioqueue_on_accept_complete(ioqueue_accept_rec *accept_overlapped)
+{
+ struct sockaddr *local;
+ struct sockaddr *remote;
+ int locallen, remotelen;
+
+ PJ_CHECK_STACK();
+
+ /* Operation complete immediately. */
+ GetAcceptExSockaddrs( accept_overlapped->accept_buf,
+ 0,
+ ACCEPT_ADDR_LEN,
+ ACCEPT_ADDR_LEN,
+ &local,
+ &locallen,
+ &remote,
+ &remotelen);
+ pj_memcpy(accept_overlapped->local, local, locallen);
+ pj_memcpy(accept_overlapped->remote, remote, locallen);
+ *accept_overlapped->addrlen = locallen;
+ if (accept_overlapped->newsock_ptr)
+ *accept_overlapped->newsock_ptr = accept_overlapped->newsock;
+ accept_overlapped->operation = 0;
+ accept_overlapped->newsock = PJ_INVALID_SOCKET;
+}
+
+static void erase_connecting_socket( pj_ioqueue_t *ioqueue, unsigned pos)
+{
+ pj_ioqueue_key_t *key = ioqueue->connecting_keys[pos];
+ HANDLE hEvent = ioqueue->connecting_handles[pos];
+ unsigned long optval;
+
+ /* Remove key from array of connecting handles. */
+ pj_array_erase(ioqueue->connecting_keys, sizeof(key),
+ ioqueue->connecting_count, pos);
+ pj_array_erase(ioqueue->connecting_handles, sizeof(HANDLE),
+ ioqueue->connecting_count, pos);
+ --ioqueue->connecting_count;
+
+ /* Disassociate the socket from the event. */
+ WSAEventSelect((pj_sock_t)key->hnd, hEvent, 0);
+
+ /* Put event object to pool. */
+ if (ioqueue->event_count < MAXIMUM_WAIT_OBJECTS) {
+ ioqueue->event_pool[ioqueue->event_count++] = hEvent;
+ } else {
+ /* Shouldn't happen. There should be no more pending connections
+ * than max.
+ */
+ pj_assert(0);
+ CloseHandle(hEvent);
+ }
+
+ /* Set socket to blocking again. */
+ optval = 0;
+ if (ioctlsocket((pj_sock_t)key->hnd, FIONBIO, &optval) != 0) {
+ DWORD dwStatus;
+ dwStatus = WSAGetLastError();
+ }
+}
+
+/*
+ * Poll for the completion of non-blocking connect().
+ * If there's a completion, the function return the key of the completed
+ * socket, and 'result' argument contains the connect() result. If connect()
+ * succeeded, 'result' will have value zero, otherwise will have the error
+ * code.
+ */
+static pj_ioqueue_key_t *check_connecting( pj_ioqueue_t *ioqueue,
+ pj_ssize_t *connect_err )
+{
+ pj_ioqueue_key_t *key = NULL;
+
+ if (ioqueue->connecting_count) {
+ DWORD result;
+
+ pj_lock_acquire(ioqueue->lock);
+ result = WaitForMultipleObjects(ioqueue->connecting_count,
+ ioqueue->connecting_handles,
+ FALSE, 0);
+ if (result >= WAIT_OBJECT_0 &&
+ result < WAIT_OBJECT_0+ioqueue->connecting_count)
+ {
+ WSANETWORKEVENTS net_events;
+
+ /* Got completed connect(). */
+ unsigned pos = result - WAIT_OBJECT_0;
+ key = ioqueue->connecting_keys[pos];
+
+ /* See whether connect has succeeded. */
+ WSAEnumNetworkEvents((pj_sock_t)key->hnd,
+ ioqueue->connecting_handles[pos],
+ &net_events);
+ *connect_err = net_events.iErrorCode[FD_CONNECT_BIT];
+
+ /* Erase socket from pending connect. */
+ erase_connecting_socket(ioqueue, pos);
+ }
+ pj_lock_release(ioqueue->lock);
+ }
+ return key;
+}
+#endif
+
+
+PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
+ pj_size_t max_fd,
+ int max_threads,
+ pj_ioqueue_t **ioqueue)
+{
+ pj_ioqueue_t *ioq;
+ pj_status_t rc;
+
+ PJ_UNUSED_ARG(max_fd);
+ PJ_ASSERT_RETURN(pool && ioqueue, PJ_EINVAL);
+
+ ioq = pj_pool_zalloc(pool, sizeof(*ioq));
+ ioq->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, max_threads);
+ if (ioq->iocp == NULL)
+ return PJ_RETURN_OS_ERROR(GetLastError());
+
+ rc = pj_lock_create_simple_mutex(pool, NULL, &ioq->lock);
+ if (rc != PJ_SUCCESS) {
+ CloseHandle(ioq->iocp);
+ return rc;
+ }
+
+ ioq->auto_delete_lock = PJ_TRUE;
+
+ *ioqueue = ioq;
+
+ PJ_LOG(4, ("pjlib", "WinNT IOCP I/O Queue created (%p)", ioq));
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioque )
+{
+ unsigned i;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(ioque, PJ_EINVAL);
+
+ /* Destroy events in the pool */
+ for (i=0; i<ioque->event_count; ++i) {
+ CloseHandle(ioque->event_pool[i]);
+ }
+ ioque->event_count = 0;
+
+ if (ioque->auto_delete_lock)
+ pj_lock_destroy(ioque->lock);
+
+ if (CloseHandle(ioque->iocp) == TRUE)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque,
+ pj_lock_t *lock,
+ pj_bool_t auto_delete )
+{
+ PJ_ASSERT_RETURN(ioque && lock, PJ_EINVAL);
+
+ if (ioque->auto_delete_lock) {
+ pj_lock_destroy(ioque->lock);
+ }
+
+ ioque->lock = lock;
+ ioque->auto_delete_lock = auto_delete;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
+ pj_ioqueue_t *ioque,
+ pj_sock_t hnd,
+ void *user_data,
+ const pj_ioqueue_callback *cb,
+ pj_ioqueue_key_t **key )
+{
+ HANDLE hioq;
+ pj_ioqueue_key_t *rec;
+
+ PJ_ASSERT_RETURN(pool && ioque && cb && key, PJ_EINVAL);
+
+ rec = pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t));
+ rec->hnd = (HANDLE)hnd;
+ rec->user_data = user_data;
+ pj_memcpy(&rec->cb, cb, sizeof(pj_ioqueue_callback));
+#if PJ_HAS_TCP
+ rec->accept_overlapped.newsock = PJ_INVALID_SOCKET;
+#endif
+ hioq = CreateIoCompletionPort((HANDLE)hnd, ioque->iocp, (DWORD)rec, 0);
+ if (!hioq) {
+ return PJ_RETURN_OS_ERROR(GetLastError());
+ }
+
+ *key = rec;
+ return PJ_SUCCESS;
+}
+
+
+
+PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key )
+{
+ PJ_ASSERT_RETURN(ioque && key, PJ_EINVAL);
+
+#if PJ_HAS_TCP
+ if (key->connecting) {
+ unsigned pos;
+
+ /* Erase from connecting_handles */
+ pj_lock_acquire(ioque->lock);
+ for (pos=0; pos < ioque->connecting_count; ++pos) {
+ if (ioque->connecting_keys[pos] == key) {
+ erase_connecting_socket(ioque, pos);
+ if (key->accept_overlapped.newsock_ptr) {
+ /* ??? shouldn't it be newsock instead of newsock_ptr??? */
+ closesocket(*key->accept_overlapped.newsock_ptr);
+ }
+ break;
+ }
+ }
+ pj_lock_release(ioque->lock);
+ key->connecting = 0;
+ }
+#endif
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
+{
+ PJ_ASSERT_RETURN(key, NULL);
+ return key->user_data;
+}
+
+/*
+ * Poll for events.
+ */
+PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout)
+{
+ DWORD dwMsec, dwBytesTransfered, dwKey;
+ ioqueue_overlapped *ov;
+ pj_ioqueue_key_t *key;
+ pj_ssize_t size_status;
+ BOOL rc;
+
+ PJ_ASSERT_RETURN(ioque, -PJ_EINVAL);
+
+ /* Check the connecting array. */
+#if PJ_HAS_TCP
+ key = check_connecting(ioque, &size_status);
+ if (key != NULL) {
+ key->cb.on_connect_complete(key, (int)size_status);
+ return 1;
+ }
+#endif
+
+ /* Calculate miliseconds timeout for GetQueuedCompletionStatus */
+ dwMsec = timeout ? timeout->sec*1000 + timeout->msec : INFINITE;
+
+ /* Poll for completion status. */
+ rc = GetQueuedCompletionStatus(ioque->iocp, &dwBytesTransfered, &dwKey,
+ (OVERLAPPED**)&ov, dwMsec);
+
+ /* The return value is:
+ * - nonzero if event was dequeued.
+ * - zero and ov==NULL if no event was dequeued.
+ * - zero and ov!=NULL if event for failed I/O was dequeued.
+ */
+ if (ov) {
+ /* Event was dequeued for either successfull or failed I/O */
+ key = (pj_ioqueue_key_t*)dwKey;
+ size_status = dwBytesTransfered;
+ switch (ov->operation) {
+ case PJ_IOQUEUE_OP_READ:
+ case PJ_IOQUEUE_OP_RECV:
+ case PJ_IOQUEUE_OP_RECV_FROM:
+ key->recv_overlapped.operation = 0;
+ if (key->cb.on_read_complete)
+ key->cb.on_read_complete(key, size_status);
+ break;
+ case PJ_IOQUEUE_OP_WRITE:
+ case PJ_IOQUEUE_OP_SEND:
+ case PJ_IOQUEUE_OP_SEND_TO:
+ key->send_overlapped.operation = 0;
+ if (key->cb.on_write_complete)
+ key->cb.on_write_complete(key, size_status);
+ break;
+#if PJ_HAS_TCP
+ case PJ_IOQUEUE_OP_ACCEPT:
+ /* special case for accept. */
+ ioqueue_on_accept_complete((ioqueue_accept_rec*)ov);
+ if (key->cb.on_accept_complete)
+ key->cb.on_accept_complete(key, key->accept_overlapped.newsock,
+ 0);
+ break;
+ case PJ_IOQUEUE_OP_CONNECT:
+#endif
+ case PJ_IOQUEUE_OP_NONE:
+ pj_assert(0);
+ break;
+ }
+ return 1;
+ }
+
+ if (GetLastError()==WAIT_TIMEOUT) {
+ /* Check the connecting array. */
+#if PJ_HAS_TCP
+ key = check_connecting(ioque, &size_status);
+ if (key != NULL) {
+ key->cb.on_connect_complete(key, (int)size_status);
+ return 1;
+ }
+#endif
+ return 0;
+ }
+ return -1;
+}
+
+/*
+ * pj_ioqueue_read()
+ *
+ * Initiate overlapped ReadFile operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen)
+{
+ BOOL rc;
+ DWORD bytesRead;
+
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(ioque);
+
+ if (key->recv_overlapped.operation != PJ_IOQUEUE_OP_NONE) {
+ pj_assert(!"Operation already pending for this descriptor");
+ return PJ_EBUSY;
+ }
+
+ pj_memset(&key->recv_overlapped, 0, sizeof(key->recv_overlapped));
+ key->recv_overlapped.operation = PJ_IOQUEUE_OP_READ;
+
+ rc = ReadFile(key->hnd, buffer, buflen, &bytesRead,
+ &key->recv_overlapped.overlapped);
+ if (rc == FALSE) {
+ DWORD dwStatus = GetLastError();
+ if (dwStatus==ERROR_IO_PENDING)
+ return PJ_EPENDING;
+ else
+ return PJ_STATUS_FROM_OS(dwStatus);
+ } else {
+ /*
+ * This is workaround to a probable bug in Win2000 (probably NT too).
+ * Even if 'rc' is TRUE, which indicates operation has completed,
+ * GetQueuedCompletionStatus still will return the key.
+ * So as work around, we always return PJ_EPENDING here.
+ */
+ return PJ_EPENDING;
+ }
+}
+
+/*
+ * pj_ioqueue_recv()
+ *
+ * Initiate overlapped WSARecv() operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen,
+ unsigned flags )
+{
+ int rc;
+ DWORD bytesRead;
+ DWORD dwFlags = 0;
+
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(ioque);
+
+ if (key->recv_overlapped.operation != PJ_IOQUEUE_OP_NONE) {
+ pj_assert(!"Operation already pending for this socket");
+ return PJ_EBUSY;
+ }
+
+ pj_memset(&key->recv_overlapped, 0, sizeof(key->recv_overlapped));
+ key->recv_overlapped.operation = PJ_IOQUEUE_OP_READ;
+
+ key->recv_overlapped.wsabuf.buf = buffer;
+ key->recv_overlapped.wsabuf.len = buflen;
+
+ dwFlags = flags;
+
+ rc = WSARecv((SOCKET)key->hnd, &key->recv_overlapped.wsabuf, 1,
+ &bytesRead, &dwFlags,
+ &key->recv_overlapped.overlapped, NULL);
+ if (rc == SOCKET_ERROR) {
+ DWORD dwStatus = WSAGetLastError();
+ if (dwStatus==WSA_IO_PENDING)
+ return PJ_EPENDING;
+ else
+ return PJ_STATUS_FROM_OS(dwStatus);
+ } else {
+ /* Must always return pending status.
+ * See comments on pj_ioqueue_read
+ * return bytesRead;
+ */
+ return PJ_EPENDING;
+ }
+}
+
+/*
+ * pj_ioqueue_recvfrom()
+ *
+ * Initiate overlapped RecvFrom() operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen,
+ unsigned flags,
+ pj_sockaddr_t *addr,
+ int *addrlen)
+{
+ BOOL rc;
+ DWORD bytesRead;
+ DWORD dwFlags;
+
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(ioque);
+
+ if (key->recv_overlapped.operation != PJ_IOQUEUE_OP_NONE) {
+ pj_assert(!"Operation already pending for this socket");
+ return PJ_EBUSY;
+ }
+
+ pj_memset(&key->recv_overlapped, 0, sizeof(key->recv_overlapped));
+ key->recv_overlapped.operation = PJ_IOQUEUE_OP_RECV_FROM;
+ key->recv_overlapped.wsabuf.buf = buffer;
+ key->recv_overlapped.wsabuf.len = buflen;
+ dwFlags = flags;
+ rc = WSARecvFrom((SOCKET)key->hnd, &key->recv_overlapped.wsabuf, 1,
+ &bytesRead, &dwFlags,
+ addr, addrlen,
+ &key->recv_overlapped.overlapped, NULL);
+ if (rc == SOCKET_ERROR) {
+ DWORD dwStatus = WSAGetLastError();
+ if (dwStatus==WSA_IO_PENDING)
+ return PJ_EPENDING;
+ else
+ return PJ_STATUS_FROM_OS(dwStatus);
+ } else {
+ /* Must always return pending status.
+ * See comments on pj_ioqueue_read
+ * return bytesRead;
+ */
+ return PJ_EPENDING;
+ }
+}
+
+/*
+ * pj_ioqueue_write()
+ *
+ * Initiate overlapped WriteFile() operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen)
+{
+ BOOL rc;
+ DWORD bytesWritten;
+
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(ioque);
+
+ if (key->send_overlapped.operation != PJ_IOQUEUE_OP_NONE) {
+ pj_assert(!"Operation already pending for this descriptor");
+ return PJ_EBUSY;
+ }
+
+ pj_memset(&key->send_overlapped, 0, sizeof(key->send_overlapped));
+ key->send_overlapped.operation = PJ_IOQUEUE_OP_WRITE;
+ rc = WriteFile(key->hnd, data, datalen, &bytesWritten,
+ &key->send_overlapped.overlapped);
+
+ if (rc == FALSE) {
+ DWORD dwStatus = GetLastError();
+ if (dwStatus==ERROR_IO_PENDING)
+ return PJ_EPENDING;
+ else
+ return PJ_STATUS_FROM_OS(dwStatus);
+ } else {
+ /* Must always return pending status.
+ * See comments on pj_ioqueue_read
+ * return bytesWritten;
+ */
+ return PJ_EPENDING;
+ }
+}
+
+
+/*
+ * pj_ioqueue_send()
+ *
+ * Initiate overlapped Send operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen,
+ unsigned flags )
+{
+ int rc;
+ DWORD bytesWritten;
+ DWORD dwFlags;
+
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(ioque);
+
+ if (key->send_overlapped.operation != PJ_IOQUEUE_OP_NONE) {
+ pj_assert(!"Operation already pending for this socket");
+ return PJ_EBUSY;
+ }
+
+ pj_memset(&key->send_overlapped, 0, sizeof(key->send_overlapped));
+ key->send_overlapped.operation = PJ_IOQUEUE_OP_WRITE;
+ key->send_overlapped.wsabuf.buf = (void*)data;
+ key->send_overlapped.wsabuf.len = datalen;
+ dwFlags = flags;
+ rc = WSASend((SOCKET)key->hnd, &key->send_overlapped.wsabuf, 1,
+ &bytesWritten, dwFlags,
+ &key->send_overlapped.overlapped, NULL);
+ if (rc == SOCKET_ERROR) {
+ DWORD dwStatus = WSAGetLastError();
+ if (dwStatus==WSA_IO_PENDING)
+ return PJ_EPENDING;
+ else
+ return PJ_STATUS_FROM_OS(dwStatus);
+ } else {
+ /* Must always return pending status.
+ * See comments on pj_ioqueue_read
+ * return bytesRead;
+ */
+ return PJ_EPENDING;
+ }
+}
+
+
+/*
+ * pj_ioqueue_sendto()
+ *
+ * Initiate overlapped SendTo operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen,
+ unsigned flags,
+ const pj_sockaddr_t *addr,
+ int addrlen)
+{
+ BOOL rc;
+ DWORD bytesSent;
+ DWORD dwFlags;
+
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(ioque);
+
+ if (key->send_overlapped.operation != PJ_IOQUEUE_OP_NONE) {
+ pj_assert(!"Operation already pending for this socket");
+ return PJ_EBUSY;
+ }
+
+ pj_memset(&key->send_overlapped, 0, sizeof(key->send_overlapped));
+ key->send_overlapped.operation = PJ_IOQUEUE_OP_SEND_TO;
+ key->send_overlapped.wsabuf.buf = (char*)data;
+ key->send_overlapped.wsabuf.len = datalen;
+ dwFlags = flags;
+ rc = WSASendTo((SOCKET)key->hnd, &key->send_overlapped.wsabuf, 1,
+ &bytesSent, dwFlags, addr,
+ addrlen, &key->send_overlapped.overlapped, NULL);
+ if (rc == SOCKET_ERROR) {
+ DWORD dwStatus = WSAGetLastError();
+ if (dwStatus==WSA_IO_PENDING)
+ return PJ_EPENDING;
+ else
+ return PJ_STATUS_FROM_OS(dwStatus);
+ } else {
+ // Must always return pending status.
+ // See comments on pj_ioqueue_read
+ // return bytesSent;
+ return PJ_EPENDING;
+ }
+}
+
+#if PJ_HAS_TCP
+
+/*
+ * pj_ioqueue_accept()
+ *
+ * Initiate overlapped accept() operation.
+ */
+PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue,
+ pj_ioqueue_key_t *key,
+ pj_sock_t *new_sock,
+ pj_sockaddr_t *local,
+ pj_sockaddr_t *remote,
+ int *addrlen)
+{
+ BOOL rc;
+ DWORD bytesReceived;
+ pj_status_t status;
+
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(ioqueue);
+
+ if (key->accept_overlapped.operation != PJ_IOQUEUE_OP_NONE) {
+ pj_assert(!"Operation already pending for this socket");
+ return PJ_EBUSY;
+ }
+
+ if (key->accept_overlapped.newsock == PJ_INVALID_SOCKET) {
+ pj_sock_t sock;
+ status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &sock);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ key->accept_overlapped.newsock = sock;
+ }
+ key->accept_overlapped.operation = PJ_IOQUEUE_OP_ACCEPT;
+ key->accept_overlapped.addrlen = addrlen;
+ key->accept_overlapped.local = local;
+ key->accept_overlapped.remote = remote;
+ key->accept_overlapped.newsock_ptr = new_sock;
+ pj_memset(&key->accept_overlapped.overlapped, 0,
+ sizeof(key->accept_overlapped.overlapped));
+
+ rc = AcceptEx( (SOCKET)key->hnd, (SOCKET)key->accept_overlapped.newsock,
+ key->accept_overlapped.accept_buf,
+ 0, ACCEPT_ADDR_LEN, ACCEPT_ADDR_LEN,
+ &bytesReceived,
+ &key->accept_overlapped.overlapped);
+
+ if (rc == TRUE) {
+ ioqueue_on_accept_complete(&key->accept_overlapped);
+ if (key->cb.on_accept_complete)
+ key->cb.on_accept_complete(key, key->accept_overlapped.newsock, 0);
+ return PJ_SUCCESS;
+ } else {
+ DWORD dwStatus = WSAGetLastError();
+ if (dwStatus==WSA_IO_PENDING)
+ return PJ_EPENDING;
+ else
+ return PJ_STATUS_FROM_OS(dwStatus);
+ }
+}
+
+
+/*
+ * pj_ioqueue_connect()
+ *
+ * Initiate overlapped connect() operation (well, it's non-blocking actually,
+ * since there's no overlapped version of connect()).
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue,
+ pj_ioqueue_key_t *key,
+ const pj_sockaddr_t *addr,
+ int addrlen )
+{
+ unsigned long optval = 1;
+ HANDLE hEvent;
+
+ PJ_CHECK_STACK();
+
+ /* Set socket to non-blocking. */
+ if (ioctlsocket((pj_sock_t)key->hnd, FIONBIO, &optval) != 0) {
+ return PJ_RETURN_OS_ERROR(WSAGetLastError());
+ }
+
+ /* Initiate connect() */
+ if (connect((pj_sock_t)key->hnd, addr, addrlen) != 0) {
+ DWORD dwStatus;
+ dwStatus = WSAGetLastError();
+ if (dwStatus != WSAEWOULDBLOCK) {
+ /* Permanent error */
+ return PJ_RETURN_OS_ERROR(dwStatus);
+ } else {
+ /* Pending operation. This is what we're looking for. */
+ }
+ } else {
+ /* Connect has completed immediately! */
+ /* Restore to blocking mode. */
+ optval = 0;
+ if (ioctlsocket((pj_sock_t)key->hnd, FIONBIO, &optval) != 0) {
+ return PJ_RETURN_OS_ERROR(WSAGetLastError());
+ }
+
+ key->cb.on_connect_complete(key, 0);
+ return PJ_SUCCESS;
+ }
+
+ /* Add to the array of connecting socket to be polled */
+ pj_lock_acquire(ioqueue->lock);
+
+ if (ioqueue->connecting_count >= MAXIMUM_WAIT_OBJECTS) {
+ pj_lock_release(ioqueue->lock);
+ return PJ_ETOOMANYCONN;
+ }
+
+ /* Get or create event object. */
+ if (ioqueue->event_count) {
+ hEvent = ioqueue->event_pool[ioqueue->event_count - 1];
+ --ioqueue->event_count;
+ } else {
+ hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (hEvent == NULL) {
+ DWORD dwStatus = GetLastError();
+ pj_lock_release(ioqueue->lock);
+ return PJ_STATUS_FROM_OS(dwStatus);
+ }
+ }
+
+ /* Mark key as connecting.
+ * We can't use array index since key can be removed dynamically.
+ */
+ key->connecting = 1;
+
+ /* Associate socket events to the event object. */
+ if (WSAEventSelect((pj_sock_t)key->hnd, hEvent, FD_CONNECT) != 0) {
+ CloseHandle(hEvent);
+ pj_lock_release(ioqueue->lock);
+ return PJ_RETURN_OS_ERROR(WSAGetLastError());
+ }
+
+ /* Add to array. */
+ ioqueue->connecting_keys[ ioqueue->connecting_count ] = key;
+ ioqueue->connecting_handles[ ioqueue->connecting_count ] = hEvent;
+ ioqueue->connecting_count++;
+
+ pj_lock_release(ioqueue->lock);
+
+ return PJ_EPENDING;
+}
+#endif /* #if PJ_HAS_TCP */
+
diff --git a/pjlib/src/pj/list.c b/pjlib/src/pj/list.c
new file mode 100644
index 00000000..82b9e83a
--- /dev/null
+++ b/pjlib/src/pj/list.c
@@ -0,0 +1,18 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/list.c 5 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/list.c $
+ *
+ * 5 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 4 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/list.h>
+
+#if !PJ_FUNCTIONS_ARE_INLINED
+# include <pj/list_i.h>
+#endif
+
+
diff --git a/pjlib/src/pj/lock.c b/pjlib/src/pj/lock.c
new file mode 100644
index 00000000..10b967a8
--- /dev/null
+++ b/pjlib/src/pj/lock.c
@@ -0,0 +1,190 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/lock.c 3 10/29/05 11:51a Bennylp $ */
+/*
+ * $Log: /pjproject-0.3/pjlib/src/pj/lock.c $
+ *
+ * 3 10/29/05 11:51a Bennylp
+ * Version 0.3-pre2.
+ *
+ * 2 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 1 10/05/05 6:35p Bennylp
+ * Created.
+ */
+#include <pj/lock.h>
+#include <pj/os.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/errno.h>
+
+
+typedef void LOCK_OBJ;
+
+/*
+ * Lock structure.
+ */
+struct pj_lock_t
+{
+ LOCK_OBJ *lock_object;
+
+ pj_status_t (*acquire) (LOCK_OBJ*);
+ pj_status_t (*tryacquire) (LOCK_OBJ*);
+ pj_status_t (*release) (LOCK_OBJ*);
+ pj_status_t (*destroy) (LOCK_OBJ*);
+};
+
+typedef pj_status_t (*FPTR)(LOCK_OBJ*);
+
+/******************************************************************************
+ * Implementation of lock object with mutex.
+ */
+static pj_lock_t mutex_lock_template =
+{
+ NULL,
+ (FPTR) &pj_mutex_lock,
+ (FPTR) &pj_mutex_trylock,
+ (FPTR) &pj_mutex_unlock,
+ (FPTR) &pj_mutex_destroy
+};
+
+static pj_status_t create_mutex_lock( pj_pool_t *pool,
+ const char *name,
+ int type,
+ pj_lock_t **lock )
+{
+ pj_lock_t *p_lock;
+ pj_status_t rc;
+
+ PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL);
+
+ p_lock = pj_pool_alloc(pool, sizeof(pj_lock_t));
+ if (!p_lock)
+ return PJ_ENOMEM;
+
+ pj_memcpy(p_lock, &mutex_lock_template, sizeof(pj_lock_t));
+ rc = pj_mutex_create(pool, name, type, (pj_mutex_t**)&p_lock->lock_object);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ *lock = p_lock;
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_lock_create_simple_mutex( pj_pool_t *pool,
+ const char *name,
+ pj_lock_t **lock )
+{
+ return create_mutex_lock(pool, name, PJ_MUTEX_SIMPLE, lock);
+}
+
+PJ_DEF(pj_status_t) pj_lock_create_recursive_mutex( pj_pool_t *pool,
+ const char *name,
+ pj_lock_t **lock )
+{
+ return create_mutex_lock(pool, name, PJ_MUTEX_RECURSE, lock);
+}
+
+
+/******************************************************************************
+ * Implementation of NULL lock object.
+ */
+static pj_status_t null_op(void *arg)
+{
+ PJ_UNUSED_ARG(arg);
+ return PJ_SUCCESS;
+}
+
+static pj_lock_t null_lock_template =
+{
+ NULL,
+ &null_op,
+ &null_op,
+ &null_op,
+ &null_op
+};
+
+PJ_DEF(pj_status_t) pj_lock_create_null_mutex( pj_pool_t *pool,
+ const char *name,
+ pj_lock_t **lock )
+{
+ PJ_UNUSED_ARG(name);
+ PJ_UNUSED_ARG(pool);
+
+ PJ_ASSERT_RETURN(lock, PJ_EINVAL);
+
+ *lock = &null_lock_template;
+ return PJ_SUCCESS;
+}
+
+
+/******************************************************************************
+ * Implementation of semaphore lock object.
+ */
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+
+static pj_lock_t sem_lock_template =
+{
+ NULL,
+ (FPTR) &pj_sem_wait,
+ (FPTR) &pj_sem_trywait,
+ (FPTR) &pj_sem_post,
+ (FPTR) &pj_sem_destroy
+};
+
+PJ_DEF(pj_status_t) pj_lock_create_semaphore( pj_pool_t *pool,
+ const char *name,
+ unsigned initial,
+ unsigned max,
+ pj_lock_t **lock )
+{
+ pj_lock_t *p_lock;
+ pj_status_t rc;
+
+ PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL);
+
+ p_lock = pj_pool_alloc(pool, sizeof(pj_lock_t));
+ if (!p_lock)
+ return PJ_ENOMEM;
+
+ pj_memcpy(p_lock, &sem_lock_template, sizeof(pj_lock_t));
+ rc = pj_sem_create( pool, name, initial, max,
+ (pj_sem_t**)&p_lock->lock_object);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ *lock = p_lock;
+
+ return PJ_SUCCESS;
+}
+
+
+#endif /* PJ_HAS_SEMAPHORE */
+
+
+PJ_DEF(pj_status_t) pj_lock_acquire( pj_lock_t *lock )
+{
+ PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
+ return (*lock->acquire)(lock->lock_object);
+}
+
+PJ_DEF(pj_status_t) pj_lock_tryacquire( pj_lock_t *lock )
+{
+ PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
+ return (*lock->tryacquire)(lock->lock_object);
+}
+
+PJ_DEF(pj_status_t) pj_lock_release( pj_lock_t *lock )
+{
+ PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
+ return (*lock->release)(lock->lock_object);
+}
+
+PJ_DEF(pj_status_t) pj_lock_destroy( pj_lock_t *lock )
+{
+ PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
+ return (*lock->destroy)(lock->lock_object);
+}
+
diff --git a/pjlib/src/pj/log.c b/pjlib/src/pj/log.c
new file mode 100644
index 00000000..7f79e55c
--- /dev/null
+++ b/pjlib/src/pj/log.c
@@ -0,0 +1,217 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/log.c 7 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/log.c $
+ *
+ * 7 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 6 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/types.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/os.h>
+#include <pj/compat/vsprintf.h>
+#include <pj/compat/stdarg.h>
+
+#if PJ_LOG_MAX_LEVEL >= 1
+
+static int log_max_level = PJ_LOG_MAX_LEVEL;
+static pj_log_func *log_writer = &pj_log_write;
+static unsigned log_decor = PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC |
+ PJ_LOG_HAS_SENDER | PJ_LOG_HAS_NEWLINE;
+
+#if PJ_LOG_USE_STACK_BUFFER==0
+static char log_buffer[PJ_LOG_MAX_SIZE];
+#endif
+
+PJ_DEF(void) pj_log_set_decor(unsigned decor)
+{
+ log_decor = decor;
+}
+
+PJ_DEF(unsigned) pj_log_get_decor(void)
+{
+ return log_decor;
+}
+
+PJ_DEF(void) pj_log_set_level(int level)
+{
+ log_max_level = level;
+}
+
+PJ_DEF(int) pj_log_get_level(void)
+{
+ return log_max_level;
+}
+
+PJ_DEF(void) pj_log_set_log_func( pj_log_func *func )
+{
+ log_writer = func;
+}
+
+PJ_DEF(pj_log_func*) pj_log_get_log_func(void)
+{
+ return log_writer;
+}
+
+static void pj_log(const char *sender, int level,
+ const char *format, va_list marker)
+{
+ pj_time_val now;
+ pj_parsed_time ptime;
+ char *pre;
+#if PJ_LOG_USE_STACK_BUFFER
+ char log_buffer[PJ_LOG_MAX_SIZE];
+#endif
+ int len;
+
+ PJ_CHECK_STACK();
+
+ if (level > log_max_level)
+ return;
+
+ /* Get current date/time. */
+ pj_gettimeofday(&now);
+ pj_time_decode(&now, &ptime);
+
+ pre = log_buffer;
+ if (log_decor & PJ_LOG_HAS_DAY_NAME) {
+ static const char *wdays[] = { "Sun", "Mon", "Tue", "Wed",
+ "Thu", "Fri", "Sat"};
+ strcpy(pre, wdays[ptime.wday]);
+ pre += 3;
+ }
+ if (log_decor & PJ_LOG_HAS_YEAR) {
+ *pre++ = ' ';
+ pre += pj_utoa(ptime.year, pre);
+ }
+ if (log_decor & PJ_LOG_HAS_MONTH) {
+ *pre++ = '-';
+ pre += pj_utoa_pad(ptime.mon, pre, 2, '0');
+ }
+ if (log_decor & PJ_LOG_HAS_DAY_OF_MON) {
+ *pre++ = ' ';
+ pre += pj_utoa_pad(ptime.day, pre, 2, '0');
+ }
+ if (log_decor & PJ_LOG_HAS_TIME) {
+ *pre++ = ' ';
+ pre += pj_utoa_pad(ptime.hour, pre, 2, '0');
+ *pre++ = ':';
+ pre += pj_utoa_pad(ptime.min, pre, 2, '0');
+ *pre++ = ':';
+ pre += pj_utoa_pad(ptime.sec, pre, 2, '0');
+ }
+ if (log_decor & PJ_LOG_HAS_MICRO_SEC) {
+ *pre++ = '.';
+ pre += pj_utoa_pad(ptime.msec, pre, 3, '0');
+ }
+ if (log_decor & PJ_LOG_HAS_SENDER) {
+ enum { SENDER_WIDTH = 12 };
+ int sender_len = strlen(sender);
+ *pre++ = ' ';
+ if (sender_len <= SENDER_WIDTH) {
+ while (sender_len < SENDER_WIDTH)
+ *pre++ = ' ', ++sender_len;
+ while (*sender)
+ *pre++ = *sender++;
+ } else {
+ int i;
+ for (i=0; i<SENDER_WIDTH; ++i)
+ *pre++ = *sender++;
+ }
+ }
+
+ if (log_decor != 0 && log_decor != PJ_LOG_HAS_NEWLINE)
+ *pre++ = ' ';
+
+ len = pre - log_buffer;
+
+ /* Print the whole message to the string log_buffer. */
+ len = len + vsnprintf(pre, sizeof(log_buffer)-len, format, marker);
+ if (len > 0 && len < sizeof(log_buffer)-1) {
+ if (log_decor & PJ_LOG_HAS_NEWLINE) {
+ log_buffer[len++] = '\n';
+ }
+ log_buffer[len++] = '\0';
+ } else {
+ len = sizeof(log_buffer)-1;
+ if (log_decor & PJ_LOG_HAS_NEWLINE) {
+ log_buffer[sizeof(log_buffer)-2] = '\n';
+ }
+ log_buffer[sizeof(log_buffer)-1] = '\0';
+ }
+
+ if (log_writer)
+ (*log_writer)(level, log_buffer, len);
+}
+
+PJ_DEF(void) pj_log_0(const char *obj, const char *format, ...)
+{
+ va_list arg;
+ va_start(arg, format);
+ pj_log(obj, 0, format, arg);
+ va_end(arg);
+}
+
+PJ_DEF(void) pj_log_1(const char *obj, const char *format, ...)
+{
+ va_list arg;
+ va_start(arg, format);
+ pj_log(obj, 1, format, arg);
+ va_end(arg);
+}
+#endif /* PJ_LOG_MAX_LEVEL >= 1 */
+
+#if PJ_LOG_MAX_LEVEL >= 2
+PJ_DEF(void) pj_log_2(const char *obj, const char *format, ...)
+{
+ va_list arg;
+ va_start(arg, format);
+ pj_log(obj, 2, format, arg);
+ va_end(arg);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 3
+PJ_DEF(void) pj_log_3(const char *obj, const char *format, ...)
+{
+ va_list arg;
+ va_start(arg, format);
+ pj_log(obj, 3, format, arg);
+ va_end(arg);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 4
+PJ_DEF(void) pj_log_4(const char *obj, const char *format, ...)
+{
+ va_list arg;
+ va_start(arg, format);
+ pj_log(obj, 4, format, arg);
+ va_end(arg);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 5
+PJ_DEF(void) pj_log_5(const char *obj, const char *format, ...)
+{
+ va_list arg;
+ va_start(arg, format);
+ pj_log(obj, 5, format, arg);
+ va_end(arg);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 6
+PJ_DEF(void) pj_log_6(const char *obj, const char *format, ...)
+{
+ va_list arg;
+ va_start(arg, format);
+ pj_log(obj, 6, format, arg);
+ va_end(arg);
+}
+#endif
+
diff --git a/pjlib/src/pj/log_writer_printk.c b/pjlib/src/pj/log_writer_printk.c
new file mode 100644
index 00000000..b18a3027
--- /dev/null
+++ b/pjlib/src/pj/log_writer_printk.c
@@ -0,0 +1,20 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/log_writer_printk.c 2 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/log_writer_printk.c $
+ *
+ * 2 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 1 9/22/05 10:37a Bennylp
+ * Created.
+ *
+ */
+#include <pj/log.h>
+#include <pj/os.h>
+
+PJ_DEF(void) pj_log_write(int level, const char *buffer, int len)
+{
+ PJ_CHECK_STACK();
+ printk(KERN_INFO "%s", buffer);
+}
+
diff --git a/pjlib/src/pj/log_writer_stdout.c b/pjlib/src/pj/log_writer_stdout.c
new file mode 100644
index 00000000..30a7a6f1
--- /dev/null
+++ b/pjlib/src/pj/log_writer_stdout.c
@@ -0,0 +1,66 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/log_writer_stdout.c 5 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/log_writer_stdout.c $
+ *
+ * 5 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 4 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 3 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/compat/stdfileio.h>
+
+#define CLR_FATAL (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R)
+#define CLR_WARNING (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R | PJ_TERM_COLOR_G)
+#define CLR_INFO (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | \
+ PJ_TERM_COLOR_B)
+#define CLR_DEFAULT (PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | PJ_TERM_COLOR_B)
+
+static void term_set_color(int level)
+{
+#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
+ unsigned attr = 0;
+ switch (level) {
+ case 0:
+ case 1: attr = CLR_FATAL;
+ break;
+ case 2: attr = CLR_WARNING;
+ break;
+ case 3: attr = CLR_INFO;
+ break;
+ default:
+ attr = CLR_DEFAULT;
+ break;
+ }
+
+ pj_term_set_color(attr);
+#endif
+}
+
+static void term_restore_color(void)
+{
+#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
+ pj_term_set_color(CLR_DEFAULT);
+#endif
+}
+
+
+PJ_DEF(void) pj_log_write(int level, const char *buffer, int len)
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(len);
+
+ /* Copy to terminal/file. */
+ term_set_color(level);
+ fputs(buffer, stdout);
+ term_restore_color();
+
+ fflush(stdout);
+}
+
diff --git a/pjlib/src/pj/md5.c b/pjlib/src/pj/md5.c
new file mode 100644
index 00000000..3c42b4ed
--- /dev/null
+++ b/pjlib/src/pj/md5.c
@@ -0,0 +1,404 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/md5.c 5 10/14/05 12:26a Bennylp $ */
+/*
+ Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+ either statically or dynamically; added missing #include <string.h>
+ in library.
+ 2002-03-11 lpd Corrected argument list for main(), and added int return
+ type, in test program and T value program.
+ 2002-02-21 lpd Added missing #include <stdio.h> in test program.
+ 2000-07-03 lpd Patched to eliminate warnings about "constant is
+ unsigned in ANSI C, signed in traditional"; made test program
+ self-checking.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+#include <pj/md5.h>
+#include <pj/string.h>
+#include <pj/os.h>
+
+#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
+
+/*
+#ifdef ARCH_IS_BIG_ENDIAN
+# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+# define BYTE_ORDER 0
+#endif
+*/
+/* pjlib: */
+#include <pj/config.h>
+#if PJ_IS_LITTLE_ENDIAN
+# define BYTE_ORDER -1
+#elif PJ_IS_BIG_ENDIAN
+# define BYTE_ORDER 1
+#else
+# error Endianess is not known!
+#endif
+
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+#if BYTE_ORDER > 0
+ /* Define storage only for big-endian CPUs. */
+ md5_word_t X[16];
+#else
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+#endif
+
+ PJ_CHECK_STACK();
+
+ {
+#if BYTE_ORDER == 0
+ /*
+ * Determine dynamically whether this is a big-endian or
+ * little-endian machine, since we can use a more efficient
+ * algorithm on the latter.
+ */
+ static const int w = 1;
+
+ if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0 /* little-endian */
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+#endif
+#if BYTE_ORDER == 0
+ else /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0 /* big-endian */
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const md5_byte_t *xp = data;
+ int i;
+
+# if BYTE_ORDER == 0
+ X = xbuf; /* (dynamic only) */
+# else
+# define xbuf X /* (static only) */
+# endif
+ for (i = 0; i < 16; ++i, xp += 4)
+ xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+ }
+#endif
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ PJ_CHECK_STACK();
+
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ PJ_CHECK_STACK();
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ PJ_CHECK_STACK();
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
diff --git a/pjlib/src/pj/os_core_linux_kernel.c b/pjlib/src/pj/os_core_linux_kernel.c
new file mode 100644
index 00000000..82edccb4
--- /dev/null
+++ b/pjlib/src/pj/os_core_linux_kernel.c
@@ -0,0 +1,685 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/os_core_linux_kernel.c 3 10/29/05 11:51a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/os_core_linux_kernel.c $
+ *
+ * 3 10/29/05 11:51a Bennylp
+ * Version 0.3-pre2.
+ *
+ * 2 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 1 9/22/05 10:38a Bennylp
+ * Creaetd.
+ *
+ */
+#include <pj/os.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/except.h>
+#include <pj/errno.h>
+#include <pj/string.h>
+#include <pj/compat/high_precision.h>
+#include <pj/compat/sprintf.h>
+
+#include <linux/config.h>
+#include <linux/version.h>
+#if defined(MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+#include <linux/kernel.h>
+#include <linux/sched.h>
+//#include <linux/tqueue.h>
+#include <linux/wait.h>
+#include <linux/signal.h>
+
+#include <asm/atomic.h>
+#include <asm/unistd.h>
+#include <asm/semaphore.h>
+
+#define THIS_FILE "oslinuxkern"
+
+struct pj_thread_t
+{
+ /** Thread's name. */
+ char obj_name[PJ_MAX_OBJ_NAME];
+
+ /** Linux task structure for thread. */
+ struct task_struct *thread;
+
+ /** Flags (specified in pj_thread_create) */
+ unsigned flags;
+
+ /** Task queue needed to launch thread. */
+ //struct tq_struct tq;
+
+ /** Semaphore needed to control thread startup. */
+ struct semaphore startstop_sem;
+
+ /** Semaphore to suspend thread during startup. */
+ struct semaphore suspend_sem;
+
+ /** Queue thread is waiting on. Gets initialized by
+ thread_initialize, can be used by thread itself.
+ */
+ wait_queue_head_t queue;
+
+ /** Flag to tell thread whether to die or not.
+ When the thread receives a signal, it must check
+ the value of terminate and call thread_deinitialize and terminate
+ if set.
+ */
+ int terminate;
+
+ /** Thread's entry. */
+ pj_thread_proc *func;
+
+ /** Argument. */
+ void *arg;
+};
+
+struct pj_atomic_t
+{
+ atomic_t atom;
+};
+
+struct pj_mutex_t
+{
+ struct semaphore sem;
+ pj_bool_t recursive;
+ pj_thread_t *owner;
+ int own_count;
+};
+
+struct pj_sem_t
+{
+ struct semaphore sem;
+};
+
+/*
+ * Static global variables.
+ */
+#define MAX_TLS_ID 32
+static void *tls_values[MAX_TLS_ID];
+static int tls_id;
+static long thread_tls_id;
+static spinlock_t critical_section = SPIN_LOCK_UNLOCKED;
+static unsigned long spinlock_flags;
+static pj_thread_t main_thread;
+
+/* private functions */
+//#define TRACE_(expr) PJ_LOG(3,expr)
+#define TRACE_(x)
+
+
+/* This must be called in the context of the new thread. */
+static void thread_initialize( pj_thread_t *thread )
+{
+ TRACE_((THIS_FILE, "---new thread initializing..."));
+
+ /* Set TLS */
+ pj_thread_local_set(thread_tls_id, thread);
+
+ /* fill in thread structure */
+ thread->thread = current;
+ pj_assert(thread->thread != NULL);
+
+ /* set signal mask to what we want to respond */
+ siginitsetinv(&current->blocked,
+ sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM));
+
+ /* initialise wait queue */
+ init_waitqueue_head(&thread->queue);
+
+ /* initialise termination flag */
+ thread->terminate = 0;
+
+ /* set name of this process (max 15 chars + 0 !) */
+ thread->obj_name[15] = '\0';
+ sprintf(current->comm, thread->obj_name);
+
+ /* tell the creator that we are ready and let him continue */
+ up(&thread->startstop_sem);
+}
+
+/* cleanup of thread. Called by the exiting thread. */
+static void thread_deinitialize(pj_thread_t *thread)
+{
+ /* we are terminating */
+
+ /* lock the kernel, the exit will unlock it */
+ thread->thread = NULL;
+ mb();
+
+ /* notify the stop_kthread() routine that we are terminating. */
+ up(&thread->startstop_sem);
+
+ /* the kernel_thread that called clone() does a do_exit here. */
+
+ /* there is no race here between execution of the "killer" and
+ real termination of the thread (race window between up and do_exit),
+ since both the thread and the "killer" function are running with
+ the kernel lock held.
+ The kernel lock will be freed after the thread exited, so the code
+ is really not executed anymore as soon as the unload functions gets
+ the kernel lock back.
+ The init process may not have made the cleanup of the process here,
+ but the cleanup can be done safely with the module unloaded.
+ */
+
+}
+
+static int thread_proc(void *arg)
+{
+ pj_thread_t *thread = arg;
+
+ TRACE_((THIS_FILE, "---new thread starting!"));
+
+ /* Initialize thread. */
+ thread_initialize( thread );
+
+ /* Wait if created suspended. */
+ if (thread->flags & PJ_THREAD_SUSPENDED) {
+ TRACE_((THIS_FILE, "---new thread suspended..."));
+ down(&thread->suspend_sem);
+ }
+
+ TRACE_((THIS_FILE, "---new thread running..."));
+
+ pj_assert(thread->func != NULL);
+
+ /* Call thread's entry. */
+ (*thread->func)(thread->arg);
+
+ TRACE_((THIS_FILE, "---thread exiting..."));
+
+ /* Cleanup thread. */
+ thread_deinitialize(thread);
+
+ return 0;
+}
+
+/* The very task entry. */
+static void kthread_launcher(void *arg)
+{
+ TRACE_((THIS_FILE, "...launching thread!..."));
+ kernel_thread(&thread_proc, arg, 0);
+}
+
+PJ_DEF(pj_status_t) pj_init(void)
+{
+ pj_status_t rc;
+
+ PJ_LOG(5, ("pj_init", "Initializing PJ Library.."));
+
+ rc = pj_thread_init();
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ /* Initialize exception ID for the pool.
+ * Must do so after critical section is configured.
+ */
+ rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_uint32_t) pj_getpid(void)
+{
+ return 1;
+}
+
+PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name,
+ pj_thread_desc desc,
+ pj_thread_t **ptr_thread)
+{
+ char stack_ptr;
+ pj_thread_t *thread = (pj_thread_t *)desc;
+ pj_str_t thread_name = pj_str((char*)cstr_thread_name);
+
+ /* Size sanity check. */
+ if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) {
+ pj_assert(!"Not enough pj_thread_desc size!");
+ return PJ_EBUG;
+ }
+
+ /* If a thread descriptor has been registered before, just return it. */
+ if (pj_thread_local_get (thread_tls_id) != 0) {
+ *ptr_thread = (pj_thread_t*)pj_thread_local_get (thread_tls_id);
+ return PJ_SUCCESS;
+ }
+
+ /* Initialize and set the thread entry. */
+ pj_memset(desc, 0, sizeof(pj_thread_desc));
+
+ if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1)
+ pj_sprintf(thread->obj_name, cstr_thread_name, thread->thread);
+ else
+ pj_sprintf(thread->obj_name, "thr%p", (void*)thread->thread);
+
+ /* Initialize. */
+ thread_initialize(thread);
+
+ /* Eat semaphore. */
+ down(&thread->startstop_sem);
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+ thread->stk_start = &stack_ptr;
+ thread->stk_size = 0xFFFFFFFFUL;
+ thread->stk_max_usage = 0;
+#else
+ stack_ptr = '\0';
+#endif
+
+ *ptr_thread = thread;
+ return PJ_SUCCESS;
+}
+
+
+pj_status_t pj_thread_init(void)
+{
+ pj_status_t rc;
+ pj_thread_t *dummy;
+
+ rc = pj_thread_local_alloc(&thread_tls_id);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ return pj_thread_register("pjlib-main", (pj_uint8_t*)&main_thread, &dummy);
+}
+
+PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, const char *thread_name,
+ pj_thread_proc *proc, void *arg,
+ pj_size_t stack_size, unsigned flags,
+ pj_thread_t **ptr_thread)
+{
+ pj_thread_t *thread;
+
+ TRACE_((THIS_FILE, "pj_thread_create()"));
+
+ PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL);
+
+ thread = pj_pool_zalloc(pool, sizeof(pj_thread_t));
+ if (!thread)
+ return PJ_ENOMEM;
+
+ PJ_UNUSED_ARG(stack_size);
+
+ /* Thread name. */
+ if (!thread_name)
+ thread_name = "thr%p";
+
+ if (strchr(thread_name, '%')) {
+ pj_snprintf(thread->obj_name, PJ_MAX_OBJ_NAME, thread_name, thread);
+ } else {
+ strncpy(thread->obj_name, thread_name, PJ_MAX_OBJ_NAME);
+ thread->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+ }
+
+ /* Init thread's semaphore. */
+ TRACE_((THIS_FILE, "...init semaphores..."));
+ init_MUTEX_LOCKED(&thread->startstop_sem);
+ init_MUTEX_LOCKED(&thread->suspend_sem);
+
+ thread->flags = flags;
+
+ if ((flags & PJ_THREAD_SUSPENDED) == 0) {
+ up(&thread->suspend_sem);
+ }
+
+ /* Store the functions and argument. */
+ thread->func = proc;
+ thread->arg = arg;
+
+ /* Save return value. */
+ *ptr_thread = thread;
+
+ /* Create the new thread by running a task through keventd. */
+
+#if 0
+ /* Initialize the task queue struct. */
+ thread->tq.sync = 0;
+ INIT_LIST_HEAD(&thread->tq.list);
+ thread->tq.routine = kthread_launcher;
+ thread->tq.data = thread;
+
+ /* and schedule it for execution. */
+ schedule_task(&thread->tq);
+#endif
+ kthread_launcher(thread);
+
+ /* Wait until thread has reached the setup_thread routine. */
+ TRACE_((THIS_FILE, "...wait for the new thread..."));
+ down(&thread->startstop_sem);
+
+ TRACE_((THIS_FILE, "...main thread resumed..."));
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *thread)
+{
+ return thread->obj_name;
+}
+
+PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *thread)
+{
+ up(&thread->suspend_sem);
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_thread_t*) pj_thread_this(void)
+{
+ return (pj_thread_t*)pj_thread_local_get(thread_tls_id);
+}
+
+PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p)
+{
+ TRACE_((THIS_FILE, "pj_thread_join()"));
+ down(&p->startstop_sem);
+ TRACE_((THIS_FILE, " joined!"));
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *thread)
+{
+ PJ_ASSERT_RETURN(thread != NULL, PJ_EINVALIDOP);
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
+{
+ pj_highprec_t ticks;
+ pj_thread_t *thread = pj_thread_this();
+
+ PJ_ASSERT_RETURN(thread != NULL, PJ_EBUG);
+
+ /* Use high precision calculation to make sure we don't
+ * crop values:
+ *
+ * ticks = HZ * msec / 1000
+ */
+ ticks = HZ;
+ pj_highprec_mul(ticks, msec);
+ pj_highprec_div(ticks, 1000);
+
+ TRACE_((THIS_FILE, "this thread will sleep for %u ticks", ticks));
+ interruptible_sleep_on_timeout( &thread->queue, ticks);
+ return PJ_SUCCESS;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool,
+ pj_atomic_value_t value,
+ pj_atomic_t **ptr_var)
+{
+ pj_atomic_t *t = pj_pool_calloc(pool, 1, sizeof(pj_atomic_t));
+ if (!t) return PJ_ENOMEM;
+
+ atomic_set(&t->atom, value);
+ *ptr_var = t;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var )
+{
+ return 0;
+}
+
+PJ_DEF(pj_atomic_value_t) pj_atomic_set(pj_atomic_t *var,
+ pj_atomic_value_t value)
+{
+ pj_atomic_value_t oldval = atomic_read(&var->atom);
+ atomic_set(&var->atom, value);
+ return oldval;
+}
+
+PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *var)
+{
+ return atomic_read(&var->atom);
+}
+
+PJ_DEF(pj_atomic_value_t) pj_atomic_inc(pj_atomic_t *var)
+{
+ atomic_inc(&var->atom);
+ return atomic_read(&var->atom);
+}
+
+PJ_DEF(pj_atomic_value_t) pj_atomic_dec(pj_atomic_t *var)
+{
+ atomic_dec(&var->atom);
+ return atomic_read(&var->atom);
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index)
+{
+ if (tls_id >= MAX_TLS_ID)
+ return PJ_ETOOMANY;
+
+ *index = tls_id++;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(void) pj_thread_local_free(long index)
+{
+ pj_assert(index >= 0 && index < MAX_TLS_ID);
+}
+
+PJ_DEF(void) pj_thread_local_set(long index, void *value)
+{
+ pj_assert(index >= 0 && index < MAX_TLS_ID);
+ tls_values[index] = value;
+}
+
+PJ_DEF(void*) pj_thread_local_get(long index)
+{
+ pj_assert(index >= 0 && index < MAX_TLS_ID);
+ return tls_values[index];
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(void) pj_enter_critical_section(void)
+{
+ spin_lock_irqsave(&critical_section, spinlock_flags);
+}
+
+PJ_DEF(void) pj_leave_critical_section(void)
+{
+ spin_unlock_irqrestore(&critical_section, spinlock_flags);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(pj_status_t) pj_mutex_create( pj_pool_t *pool,
+ const char *name,
+ int type,
+ pj_mutex_t **ptr_mutex)
+{
+ pj_mutex_t *mutex;
+
+ PJ_UNUSED_ARG(name);
+
+ mutex = pj_pool_alloc(pool, sizeof(pj_mutex_t));
+ if (!mutex)
+ return PJ_ENOMEM;
+
+ init_MUTEX(&mutex->sem);
+
+ mutex->recursive = (type == PJ_MUTEX_RECURSE);
+ mutex->owner = NULL;
+ mutex->own_count = 0;
+
+ /* Done. */
+ *ptr_mutex = mutex;
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, const char *name,
+ pj_mutex_t **mutex )
+{
+ return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex);
+}
+
+PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
+ const char *name,
+ pj_mutex_t **mutex )
+{
+ return pj_mutex_create( pool, name, PJ_MUTEX_RECURSE, mutex);
+}
+
+PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
+{
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+ if (mutex->recursive) {
+ pj_thread_t *this_thread = pj_thread_this();
+ if (mutex->owner == this_thread) {
+ ++mutex->own_count;
+ } else {
+ down(&mutex->sem);
+ pj_assert(mutex->own_count == 0);
+ mutex->owner = this_thread;
+ mutex->own_count = 1;
+ }
+ } else {
+ down(&mutex->sem);
+ }
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
+{
+ long rc;
+
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+ if (mutex->recursive) {
+ pj_thread_t *this_thread = pj_thread_this();
+ if (mutex->owner == this_thread) {
+ ++mutex->own_count;
+ } else {
+ rc = down_interruptible(&mutex->sem);
+ if (rc != 0)
+ return PJ_RETURN_OS_ERROR(-rc);
+ pj_assert(mutex->own_count == 0);
+ mutex->owner = this_thread;
+ mutex->own_count = 1;
+ }
+ } else {
+ int rc = down_trylock(&mutex->sem);
+ if (rc != 0)
+ return PJ_RETURN_OS_ERROR(-rc);
+ }
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
+{
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+ if (mutex->recursive) {
+ pj_thread_t *this_thread = pj_thread_this();
+ if (mutex->owner == this_thread) {
+ pj_assert(mutex->own_count > 0);
+ --mutex->own_count;
+ if (mutex->own_count == 0) {
+ mutex->owner = NULL;
+ up(&mutex->sem);
+ }
+ } else {
+ pj_assert(!"Not owner!");
+ return PJ_EINVALIDOP;
+ }
+ } else {
+ up(&mutex->sem);
+ }
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
+{
+ PJ_ASSERT_RETURN(mutex != NULL, PJ_EINVAL);
+
+ return PJ_SUCCESS;
+}
+
+#if defined(PJ_DEBUG) && PJ_DEBUG != 0
+PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex)
+{
+ if (mutex->recursive)
+ return mutex->owner == pj_thread_this();
+ else
+ return 1;
+}
+#endif /* PJ_DEBUG */
+
+
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+
+PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool,
+ const char *name,
+ unsigned initial,
+ unsigned max,
+ pj_sem_t **sem)
+{
+ pj_sem_t *sem;
+
+ PJ_UNUSED_ARG(max);
+
+ PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
+
+ sem = pj_pool_alloc(pool, sizeof(pj_sem_t));
+ sema_init(&sem->sem, initial);
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
+{
+ PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
+
+ down(&sem->sem);
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
+{
+ int rc;
+
+ PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
+
+ rc = down_trylock(&sem->sem);
+ if (rc != 0) {
+ return PJ_RETURN_OS_ERROR(-rc);
+ } else {
+ return PJ_SUCCESS;
+ }
+}
+
+PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
+{
+ PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
+
+ up(&sem->sem);
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
+{
+ PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
+
+ return PJ_SUCCESS;
+}
+
+#endif /* PJ_HAS_SEMAPHORE */
+
+
+
+
diff --git a/pjlib/src/pj/os_core_unix.c b/pjlib/src/pj/os_core_unix.c
new file mode 100644
index 00000000..0f4f3a99
--- /dev/null
+++ b/pjlib/src/pj/os_core_unix.c
@@ -0,0 +1,1182 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/os_core_unix.c 11 10/29/05 10:27p Bennylp $ */
+/*
+ * $Log: /pjproject-0.3/pjlib/src/pj/os_core_unix.c $
+ *
+ * 11 10/29/05 10:27p Bennylp
+ * Fixed misc warnings.
+ *
+ * 10 10/29/05 11:51a Bennylp
+ * Version 0.3-pre2.
+ *
+ * 9 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ */
+#include <pj/os.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/rand.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/compat/sprintf.h>
+#include <pj/except.h>
+#include <pj/errno.h>
+
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+# include <semaphore.h>
+#endif
+
+#include <unistd.h> // getpid()
+#include <errno.h> // errno
+
+#define __USE_GNU
+#include <pthread.h>
+
+#define THIS_FILE "osunix"
+
+struct pj_thread_t
+{
+ char obj_name[PJ_MAX_OBJ_NAME];
+ pthread_t thread;
+ pj_thread_proc *proc;
+ void *arg;
+
+ pj_mutex_t *suspended_mutex;
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+ pj_uint32_t stk_size;
+ pj_uint32_t stk_max_usage;
+ char *stk_start;
+ const char *caller_file;
+ int caller_line;
+#endif
+};
+
+struct pj_atomic_t
+{
+ pj_mutex_t *mutex;
+ pj_atomic_value_t value;
+};
+
+struct pj_mutex_t
+{
+ pthread_mutex_t mutex;
+ char obj_name[PJ_MAX_OBJ_NAME];
+#if PJ_DEBUG
+ int nesting_level;
+ pj_thread_t *owner;
+#endif
+};
+
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+struct pj_sem_t
+{
+ sem_t sem;
+ char obj_name[PJ_MAX_OBJ_NAME];
+};
+#endif /* PJ_HAS_SEMAPHORE */
+
+#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
+struct pj_event_t
+{
+ char obj_name[PJ_MAX_OBJ_NAME];
+};
+#endif /* PJ_HAS_EVENT_OBJ */
+
+
+#if PJ_HAS_THREADS
+ static pj_thread_t main_thread;
+ static long thread_tls_id;
+ static pj_mutex_t critical_section;
+#else
+# define MAX_THREADS 32
+ static int tls_flag[MAX_THREADS];
+ static void *tls[MAX_THREADS];
+#endif
+
+static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type);
+
+/*
+ * pj_init(void).
+ * Init PJLIB!
+ */
+PJ_DEF(pj_status_t) pj_init(void)
+{
+ char dummy_guid[PJ_GUID_MAX_LENGTH];
+ pj_str_t guid;
+ pj_status_t rc;
+
+ PJ_LOG(5, ("pj_init", "Initializing PJ Library.."));
+
+#if PJ_HAS_THREADS
+ /* Init this thread's TLS. */
+ if ((rc=pj_thread_init()) != 0) {
+ return rc;
+ }
+
+ /* Critical section. */
+ if ((rc=init_mutex(&critical_section, "critsec", PJ_MUTEX_SIMPLE)) != 0)
+ return rc;
+
+#endif
+
+ /* Initialize exception ID for the pool.
+ * Must do so after critical section is configured.
+ */
+ rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ /* Init random seed. */
+ pj_srand( clock() );
+
+ /* Startup GUID. */
+ guid.ptr = dummy_guid;
+ pj_generate_unique_string( &guid );
+
+ /* Initialize exception ID for the pool.
+ * Must do so after critical section is configured.
+ */
+ rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ /* Startup timestamp */
+#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
+ {
+ pj_timestamp dummy_ts;
+ if ((rc=pj_get_timestamp(&dummy_ts)) != 0) {
+ return rc;
+ }
+ }
+#endif
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_getpid(void)
+ */
+PJ_DEF(pj_uint32_t) pj_getpid(void)
+{
+ PJ_CHECK_STACK();
+ return getpid();
+}
+
+/*
+ * pj_thread_register(..)
+ */
+PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name,
+ pj_thread_desc desc,
+ pj_thread_t **ptr_thread)
+{
+#if PJ_HAS_THREADS
+ char stack_ptr;
+ pj_thread_t *thread = (pj_thread_t *)desc;
+ pj_str_t thread_name = pj_str((char*)cstr_thread_name);
+
+ /* Size sanity check. */
+ if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) {
+ pj_assert(!"Not enough pj_thread_desc size!");
+ return PJ_EBUG;
+ }
+
+ /* If a thread descriptor has been registered before, just return it. */
+ if (pj_thread_local_get (thread_tls_id) != 0) {
+ *ptr_thread = (pj_thread_t*)pj_thread_local_get (thread_tls_id);
+ return PJ_SUCCESS;
+ }
+
+ /* Initialize and set the thread entry. */
+ pj_memset(desc, 0, sizeof(pj_thread_desc));
+ thread->thread = pthread_self();
+
+ if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1)
+ pj_sprintf(thread->obj_name, cstr_thread_name, thread->thread);
+ else
+ pj_sprintf(thread->obj_name, "thr%p", (void*)thread->thread);
+
+ pj_thread_local_set(thread_tls_id, thread);
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+ thread->stk_start = &stack_ptr;
+ thread->stk_size = 0xFFFFFFFFUL;
+ thread->stk_max_usage = 0;
+#else
+ stack_ptr = '\0';
+#endif
+
+ *ptr_thread = thread;
+ return PJ_SUCCESS;
+#else
+ pj_thread_t *thread = (pj_thread_t*)desc;
+ *ptr_thread = thread;
+ return SUCCESS;
+#endif
+}
+
+/*
+ * pj_thread_init(void)
+ */
+pj_status_t pj_thread_init(void)
+{
+#if PJ_HAS_THREADS
+ pj_status_t rc;
+ pj_thread_t *dummy;
+
+ rc = pj_thread_local_alloc(&thread_tls_id );
+ if (rc != PJ_SUCCESS) {
+ return rc;
+ }
+ return pj_thread_register("thr%p", (pj_uint8_t*)&main_thread, &dummy);
+#else
+ PJ_LOG(2,(THIS_FILE, "Thread init error. Threading is not enabled!"));
+ return PJ_EINVALIDOP;
+#endif
+}
+
+#if PJ_HAS_THREADS
+/*
+ * thread_main()
+ *
+ * This is the main entry for all threads.
+ */
+static void *thread_main(void *param)
+{
+ pj_thread_t *rec = param;
+ void *result;
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+ rec->stk_start = (char*)&rec;
+#endif
+
+ /* Set current thread id. */
+ pj_thread_local_set(thread_tls_id, rec);
+
+ /* Check if suspension is required. */
+ if (rec->suspended_mutex)
+ pj_mutex_lock(rec->suspended_mutex);
+
+ PJ_LOG(6,(rec->obj_name, "Thread started"));
+
+ /* Call user's entry! */
+ result = (void*) (*rec->proc)(rec->arg);
+
+ /* Done. */
+ PJ_LOG(6,(rec->obj_name, "Thread quitting"));
+ return result;
+}
+#endif
+
+/*
+ * pj_thread_create(...)
+ */
+PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool,
+ const char *thread_name,
+ pj_thread_proc *proc,
+ void *arg,
+ pj_size_t stack_size,
+ unsigned flags,
+ pj_thread_t **ptr_thread)
+{
+#if PJ_HAS_THREADS
+ pj_thread_t *rec;
+ int rc;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL);
+
+ /* Create thread record and assign name for the thread */
+ rec = (struct pj_thread_t*) pj_pool_zalloc(pool, sizeof(pj_thread_t));
+ if (!rec)
+ return PJ_ENOMEM;
+
+ /* Set name. */
+ if (!thread_name)
+ thread_name = "thr%p";
+
+ if (strchr(thread_name, '%')) {
+ pj_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec);
+ } else {
+ strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME);
+ rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+ }
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+ rec->stk_size = stack_size ? stack_size : 0xFFFFFFFFUL;
+ rec->stk_max_usage = 0;
+#endif
+
+ /* Emulate suspended thread with mutex. */
+ if (flags & PJ_THREAD_SUSPENDED) {
+ rc = pj_mutex_create_simple(pool, NULL, &rec->suspended_mutex);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ pj_mutex_lock(rec->suspended_mutex);
+ } else {
+ pj_assert(rec->suspended_mutex == NULL);
+ }
+
+ PJ_LOG(6, (rec->obj_name, "Thread created"));
+
+ /* Create the thread. */
+ rec->proc = proc;
+ rec->arg = arg;
+ rc = pthread_create( &rec->thread, NULL, thread_main, rec);
+ if (rc != 0)
+ return PJ_RETURN_OS_ERROR(rc);
+
+ *ptr_thread = rec;
+ return PJ_SUCCESS;
+#else
+ pj_assert(!"Threading is disabled!");
+ return PJ_EINVALIDOP;
+#endif
+}
+
+/*
+ * pj_thread-get_name()
+ */
+PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p)
+{
+#if PJ_HAS_THREADS
+ pj_thread_t *rec = (pj_thread_t*)p;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(p, "");
+
+ return rec->obj_name;
+#else
+ return "";
+#endif
+}
+
+/*
+ * pj_thread_resume()
+ */
+PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p)
+{
+ pj_status_t rc;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(p, PJ_EINVAL);
+
+ rc = pj_mutex_unlock(p->suspended_mutex);
+
+ return rc;
+}
+
+/*
+ * pj_thread_this()
+ */
+PJ_DEF(pj_thread_t*) pj_thread_this(void)
+{
+#if PJ_HAS_THREADS
+ pj_thread_t *rec = pj_thread_local_get(thread_tls_id);
+ pj_assert(rec != NULL);
+
+ /*
+ * MUST NOT check stack because this function is called
+ * by PJ_CHECK_STACK() itself!!!
+ *
+ */
+
+ return rec;
+#else
+ pj_assert(!"Threading is not enabled!");
+ return NULL;
+#endif
+}
+
+/*
+ * pj_thread_join()
+ */
+PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p)
+{
+#if PJ_HAS_THREADS
+ pj_thread_t *rec = (pj_thread_t *)p;
+ void *ret;
+ int result;
+
+ PJ_CHECK_STACK();
+
+ PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name));
+ result = pthread_join( rec->thread, &ret);
+
+ if (result == 0)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(result);
+#else
+ PJ_CHECK_STACK();
+ pj_assert(!"No multithreading support!");
+ return PJ_EINVALIDOP;
+#endif
+}
+
+/*
+ * pj_thread_destroy()
+ */
+PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p)
+{
+ /* This function is used to destroy thread handle in other platforms.
+ * I suppose there's nothing to do here..
+ */
+ PJ_CHECK_STACK();
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_thread_sleep()
+ */
+PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
+{
+ PJ_CHECK_STACK();
+ return usleep(msec * 1000);
+}
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+/*
+ * pj_thread_check_stack()
+ * Implementation for PJ_CHECK_STACK()
+ */
+PJ_DEF(void) pj_thread_check_stack(const char *file, int line)
+{
+ char stk_ptr;
+ pj_uint32_t usage;
+ pj_thread_t *thread = pj_thread_this();
+
+ /* Calculate current usage. */
+ usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start :
+ thread->stk_start - &stk_ptr;
+
+ /* Assert if stack usage is dangerously high. */
+ pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128));
+
+ /* Keep statistic. */
+ if (usage > thread->stk_max_usage) {
+ thread->stk_max_usage = usage;
+ thread->caller_file = file;
+ thread->caller_line = line;
+ }
+}
+
+/*
+ * pj_thread_get_stack_max_usage()
+ */
+PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread)
+{
+ return thread->stk_max_usage;
+}
+
+/*
+ * pj_thread_get_stack_info()
+ */
+PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread,
+ const char **file,
+ int *line )
+{
+ pj_assert(thread);
+
+ *file = thread->caller_file;
+ *line = thread->caller_line;
+ return 0;
+}
+
+#endif /* PJ_OS_HAS_CHECK_STACK */
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * pj_atomic_create()
+ */
+PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool,
+ pj_atomic_value_t initial,
+ pj_atomic_t **ptr_atomic)
+{
+ pj_status_t rc;
+ pj_atomic_t *atomic_var = pj_pool_calloc(pool, 1, sizeof(pj_atomic_t));
+ if (!atomic_var)
+ return PJ_ENOMEM;
+
+#if PJ_HAS_THREADS
+ rc = pj_mutex_create(pool, "atm%p", PJ_MUTEX_SIMPLE, &atomic_var->mutex);
+ if (rc != PJ_SUCCESS)
+ return rc;
+#endif
+ atomic_var->value = initial;
+
+ *ptr_atomic = atomic_var;
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_atomic_destroy()
+ */
+PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var )
+{
+ PJ_ASSERT_RETURN(atomic_var, PJ_EINVAL);
+#if PJ_HAS_THREADS
+ return pj_mutex_destroy( atomic_var->mutex );
+#else
+ return 0;
+#endif
+}
+
+/*
+ * pj_atomic_set()
+ */
+PJ_DEF(pj_atomic_value_t) pj_atomic_set(pj_atomic_t *atomic_var,
+ pj_atomic_value_t value)
+{
+ pj_atomic_value_t oldval;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(atomic_var, 0);
+
+#if PJ_HAS_THREADS
+ pj_mutex_lock( atomic_var->mutex );
+#endif
+ oldval = atomic_var->value;
+ atomic_var->value = value;
+#if PJ_HAS_THREADS
+ pj_mutex_unlock( atomic_var->mutex);
+#endif
+ return oldval;
+}
+
+/*
+ * pj_atomic_get()
+ */
+PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var)
+{
+ pj_atomic_value_t oldval;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(atomic_var, 0);
+
+#if PJ_HAS_THREADS
+ pj_mutex_lock( atomic_var->mutex );
+#endif
+ oldval = atomic_var->value;
+#if PJ_HAS_THREADS
+ pj_mutex_unlock( atomic_var->mutex);
+#endif
+ return oldval;
+}
+
+/*
+ * pj_atomic_inc()
+ */
+PJ_DEF(pj_atomic_value_t) pj_atomic_inc(pj_atomic_t *atomic_var)
+{
+ pj_atomic_value_t newval;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(atomic_var, 0);
+
+#if PJ_HAS_THREADS
+ pj_mutex_lock( atomic_var->mutex );
+#endif
+ newval = ++atomic_var->value;
+#if PJ_HAS_THREADS
+ pj_mutex_unlock( atomic_var->mutex);
+#endif
+ return newval;
+}
+
+/*
+ * pj_atomic_dec()
+ */
+PJ_DEF(pj_atomic_value_t) pj_atomic_dec(pj_atomic_t *atomic_var)
+{
+ pj_atomic_value_t newval;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(atomic_var, 0);
+
+#if PJ_HAS_THREADS
+ pj_mutex_lock( atomic_var->mutex );
+#endif
+ newval = --atomic_var->value;
+#if PJ_HAS_THREADS
+ pj_mutex_unlock( atomic_var->mutex);
+#endif
+ return newval;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * pj_thread_local_alloc()
+ */
+PJ_DEF(pj_status_t) pj_thread_local_alloc(long *p_index)
+{
+#if PJ_HAS_THREADS
+ pthread_key_t key;
+ int rc;
+
+ PJ_ASSERT_RETURN(p_index != NULL, PJ_EINVAL);
+
+ pj_assert( sizeof(pthread_key_t) <= sizeof(long));
+ if ((rc=pthread_key_create(&key, NULL)) != 0)
+ return PJ_RETURN_OS_ERROR(rc);
+
+ *p_index = key;
+ return PJ_SUCCESS;
+#else
+ int i;
+ for (i=0; i<MAX_THREADS; ++i) {
+ if (tls_flag[i] == 0)
+ break;
+ }
+ if (i == MAX_THREADS)
+ return PJ_ETOOMANY;
+
+ tls_flag[i] = 1;
+ tls[i] = NULL;
+
+ *p_index = i;
+ return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_thread_local_free()
+ */
+PJ_DEF(void) pj_thread_local_free(long index)
+{
+ PJ_CHECK_STACK();
+#if PJ_HAS_THREADS
+ pthread_key_delete(index);
+#else
+ tls_flag[index] = 0;
+#endif
+}
+
+/*
+ * pj_thread_local_set()
+ */
+PJ_DEF(void) pj_thread_local_set(long index, void *value)
+{
+ //Can't check stack because this function is called in the
+ //beginning before main thread is initialized.
+ //PJ_CHECK_STACK();
+#if PJ_HAS_THREADS
+ pthread_setspecific(index, value);
+#else
+ pj_assert(index >= 0 && index < MAX_THREADS);
+ tls[index] = value;
+#endif
+}
+
+PJ_DEF(void*) pj_thread_local_get(long index)
+{
+ //Can't check stack because this function is called
+ //by PJ_CHECK_STACK() itself!!!
+ //PJ_CHECK_STACK();
+#if PJ_HAS_THREADS
+ return pthread_getspecific(index);
+#else
+ pj_assert(index >= 0 && index < MAX_THREADS);
+ return tls[index];
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(void) pj_enter_critical_section(void)
+{
+#if PJ_HAS_THREADS
+ pj_mutex_lock(&critical_section);
+#endif
+}
+
+PJ_DEF(void) pj_leave_critical_section(void)
+{
+#if PJ_HAS_THREADS
+ pj_mutex_unlock(&critical_section);
+#endif
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type)
+{
+#if PJ_HAS_THREADS
+ PJ_UNUSED_ARG(type);
+
+ PJ_CHECK_STACK();
+
+ if (type == PJ_MUTEX_SIMPLE) {
+ pthread_mutex_t the_mutex = PTHREAD_MUTEX_INITIALIZER;
+ mutex->mutex = the_mutex;
+ } else {
+ pthread_mutex_t the_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+ mutex->mutex = the_mutex;
+ }
+
+#if PJ_DEBUG
+ /* Set owner. */
+ mutex->nesting_level = 0;
+ mutex->owner = NULL;
+#endif
+
+ /* Set name. */
+ if (!name) {
+ name = "mtx%p";
+ }
+ if (strchr(name, '%')) {
+ pj_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex);
+ } else {
+ strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME);
+ mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+ }
+
+ PJ_LOG(6, (mutex->obj_name, "Mutex created"));
+ return PJ_SUCCESS;
+#else /* PJ_HAS_THREADS */
+ return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_mutex_create()
+ */
+PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool,
+ const char *name,
+ int type,
+ pj_mutex_t **ptr_mutex)
+{
+#if PJ_HAS_THREADS
+ pj_status_t rc;
+ pj_mutex_t *mutex;
+
+ PJ_ASSERT_RETURN(pool && ptr_mutex, PJ_EINVAL);
+
+ mutex = pj_pool_alloc(pool, sizeof(*mutex));
+ if (!mutex) return PJ_ENOMEM;
+
+ if ((rc=init_mutex(mutex, name, type)) != PJ_SUCCESS)
+ return rc;
+
+ *ptr_mutex = mutex;
+ return PJ_SUCCESS;
+#else /* PJ_HAS_THREADS */
+ return (pj_mutex_t*)1;
+#endif
+}
+
+/*
+ * pj_mutex_create_simple()
+ */
+PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool,
+ const char *name,
+ pj_mutex_t **mutex )
+{
+ return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex);
+}
+
+/*
+ * pj_mutex_create_recursive()
+ */
+PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
+ const char *name,
+ pj_mutex_t **mutex )
+{
+ return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex);
+}
+
+/*
+ * pj_mutex_lock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
+{
+#if PJ_HAS_THREADS
+ pj_status_t status;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+ PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting",
+ pj_thread_this()->obj_name));
+
+ status = pthread_mutex_lock( &mutex->mutex );
+
+ PJ_LOG(6,(mutex->obj_name,
+ (status==0 ? "Mutex acquired by thread %s" : "FAILED by %s"),
+ pj_thread_this()->obj_name));
+
+#if PJ_DEBUG
+ if (status == PJ_SUCCESS) {
+ mutex->owner = pj_thread_this();
+ ++mutex->nesting_level;
+ }
+#endif
+
+ if (status == 0)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(status);
+#else /* PJ_HAS_THREADS */
+ pj_assert( mutex == (pj_mutex_t*)1 );
+ return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_mutex_unlock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
+{
+#if PJ_HAS_THREADS
+ pj_status_t status;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+#if PJ_DEBUG
+ pj_assert(mutex->owner == pj_thread_this());
+ if (--mutex->nesting_level == 0) {
+ mutex->owner = NULL;
+ }
+#endif
+
+ PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s",
+ pj_thread_this()->obj_name));
+
+ status = pthread_mutex_unlock( &mutex->mutex );
+ if (status == 0)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(status);
+
+#else /* PJ_HAS_THREADS */
+ pj_assert( mutex == (pj_mutex_t*)1 );
+ return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_mutex_trylock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
+{
+#if PJ_HAS_THREADS
+ int status;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+ status = pthread_mutex_trylock( &mutex->mutex );
+
+ if (status==0) {
+ PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s",
+ pj_thread_this()->obj_name));
+
+#if PJ_DEBUG
+ mutex->owner = pj_thread_this();
+ ++mutex->nesting_level;
+#endif
+ }
+
+ if (status==0)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(status);
+#else /* PJ_HAS_THREADS */
+ pj_assert( mutex == (pj_mutex_t*)1);
+ return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_mutex_destroy()
+ */
+PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
+{
+ int status;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+#if PJ_HAS_THREADS
+ PJ_LOG(6,(mutex->obj_name, "Mutex destroyed"));
+ status = pthread_mutex_destroy( &mutex->mutex );
+ if (status == 0)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(status);
+#else
+ pj_assert( mutex == (pj_mutex_t*)1 );
+ status = PJ_SUCCESS;
+ return status;
+#endif
+}
+
+#if PJ_DEBUG
+PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex)
+{
+#if PJ_HAS_THREADS
+ return mutex->owner == pj_thread_this();
+#else
+ return 1;
+#endif
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+
+/*
+ * pj_sem_create()
+ */
+PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool,
+ const char *name,
+ unsigned initial,
+ unsigned max,
+ pj_sem_t **ptr_sem)
+{
+#if PJ_HAS_THREADS
+ pj_sem_t *sem;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(pool != NULL && ptr_sem != NULL, PJ_EINVAL);
+
+ sem = pj_pool_alloc(pool, sizeof(*sem));
+ if (!sem) return PJ_ENOMEM;
+
+ if (sem_init( &sem->sem, 0, initial) != 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+
+ /* Set name. */
+ if (!name) {
+ name = "sem%p";
+ }
+ if (strchr(name, '%')) {
+ pj_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem);
+ } else {
+ strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME);
+ sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+ }
+
+ PJ_LOG(6, (sem->obj_name, "Semaphore created"));
+
+ *ptr_sem = sem;
+ return PJ_SUCCESS;
+#else
+ *ptr_sem = (pj_sem_t*)1;
+ return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_sem_wait()
+ */
+PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
+{
+#if PJ_HAS_THREADS
+ int result;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+ PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting",
+ pj_thread_this()->obj_name));
+
+ result = sem_wait( &sem->sem );
+
+ if (result == 0) {
+ PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s",
+ pj_thread_this()->obj_name));
+ } else {
+ PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire",
+ pj_thread_this()->obj_name));
+ }
+
+ if (result == 0)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+#else
+ pj_assert( sem == (pj_sem_t*) 1 );
+ return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_sem_trywait()
+ */
+PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
+{
+#if PJ_HAS_THREADS
+ int result;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+ result = sem_trywait( &sem->sem );
+
+ if (result == 0) {
+ PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s",
+ pj_thread_this()->obj_name));
+ }
+ if (result == 0)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+#else
+ pj_assert( sem == (pj_sem_t*)1 );
+ return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_sem_post()
+ */
+PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
+{
+#if PJ_HAS_THREADS
+ int result;
+ PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s",
+ pj_thread_this()->obj_name));
+ result = sem_post( &sem->sem );
+
+ if (result == 0)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+#else
+ pj_assert( sem == (pj_sem_t*) 1);
+ return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_sem_destroy()
+ */
+PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
+{
+#if PJ_HAS_THREADS
+ int result;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+ PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s",
+ pj_thread_this()->obj_name));
+ result = sem_destroy( &sem->sem );
+
+ if (result == 0)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+#else
+ pj_assert( sem == (pj_sem_t*) 1 );
+ return PJ_SUCCESS;
+#endif
+}
+
+#endif /* PJ_HAS_SEMAPHORE */
+
+///////////////////////////////////////////////////////////////////////////////
+#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
+
+/*
+ * pj_event_create()
+ */
+PJ_DEF(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name,
+ pj_bool_t manual_reset, pj_bool_t initial,
+ pj_event_t **ptr_event)
+{
+ pj_assert(!"Not supported!");
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(name);
+ PJ_UNUSED_ARG(manual_reset);
+ PJ_UNUSED_ARG(initial);
+ PJ_UNUSED_ARG(ptr_event);
+ return PJ_EINVALIDOP;
+}
+
+/*
+ * pj_event_wait()
+ */
+PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event)
+{
+ PJ_UNUSED_ARG(event);
+ return PJ_EINVALIDOP;
+}
+
+/*
+ * pj_event_trywait()
+ */
+PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event)
+{
+ PJ_UNUSED_ARG(event);
+ return PJ_EINVALIDOP;
+}
+
+/*
+ * pj_event_set()
+ */
+PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event)
+{
+ PJ_UNUSED_ARG(event);
+ return PJ_EINVALIDOP;
+}
+
+/*
+ * pj_event_pulse()
+ */
+PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event)
+{
+ PJ_UNUSED_ARG(event);
+ return PJ_EINVALIDOP;
+}
+
+/*
+ * pj_event_reset()
+ */
+PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event)
+{
+ PJ_UNUSED_ARG(event);
+ return PJ_EINVALIDOP;
+}
+
+/*
+ * pj_event_destroy()
+ */
+PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event)
+{
+ PJ_UNUSED_ARG(event);
+ return PJ_EINVALIDOP;
+}
+
+#endif /* PJ_HAS_EVENT_OBJ */
+
+///////////////////////////////////////////////////////////////////////////////
+#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
+/*
+ * Terminal
+ */
+
+/**
+ * Set terminal color.
+ */
+PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color)
+{
+ PJ_UNUSED_ARG(color);
+ return PJ_EINVALIDOP;
+}
+
+/**
+ * Get current terminal foreground color.
+ */
+PJ_DEF(pj_color_t) pj_term_get_color(void)
+{
+ return 0;
+}
+
+#endif /* PJ_TERM_HAS_COLOR */
+
diff --git a/pjlib/src/pj/os_core_win32.c b/pjlib/src/pj/os_core_win32.c
new file mode 100644
index 00000000..dd892b5d
--- /dev/null
+++ b/pjlib/src/pj/os_core_win32.c
@@ -0,0 +1,1182 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/os_core_win32.c 12 10/29/05 11:51a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/os_core_win32.c $
+ *
+ * 12 10/29/05 11:51a Bennylp
+ * Version 0.3-pre2.
+ *
+ * 11 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 10 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 9 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/rand.h>
+#include <pj/assert.h>
+#include <pj/compat/vsprintf.h>
+#include <pj/compat/sprintf.h>
+#include <pj/errno.h>
+#include <pj/except.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#if defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0
+# include <winsock.h>
+#endif
+
+#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0
+# include <winsock2.h>
+#endif
+
+/*
+ * Implementation of pj_thread_t.
+ */
+struct pj_thread_t
+{
+ char obj_name[PJ_MAX_OBJ_NAME];
+ HANDLE hthread;
+ DWORD idthread;
+ pj_thread_proc *proc;
+ void *arg;
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+ pj_uint32_t stk_size;
+ pj_uint32_t stk_max_usage;
+ char *stk_start;
+ const char *caller_file;
+ int caller_line;
+#endif
+};
+
+
+/*
+ * Implementation of pj_mutex_t.
+ */
+struct pj_mutex_t
+{
+#if PJ_WIN32_WINNT >= 0x0400
+ CRITICAL_SECTION crit;
+#else
+ HANDLE hMutex;
+#endif
+ char obj_name[PJ_MAX_OBJ_NAME];
+#if PJ_DEBUG
+ int nesting_level;
+ pj_thread_t *owner;
+#endif
+};
+
+/*
+ * Implementation of pj_sem_t.
+ */
+typedef struct pj_sem_t
+{
+ HANDLE hSemaphore;
+ char obj_name[PJ_MAX_OBJ_NAME];
+} pj_mem_t;
+
+
+/*
+ * Implementation of pj_event_t.
+ */
+struct pj_event_t
+{
+ HANDLE hEvent;
+ char obj_name[PJ_MAX_OBJ_NAME];
+};
+
+/*
+ * Implementation of pj_atomic_t.
+ */
+struct pj_atomic_t
+{
+ long value;
+};
+
+/*
+ * Static global variables.
+ */
+static pj_thread_desc main_thread;
+static long thread_tls_id;
+static pj_mutex_t critical_section_mutex;
+
+
+/*
+ * Some static prototypes.
+ */
+static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name);
+
+
+/*
+ * pj_init(void).
+ * Init PJLIB!
+ */
+PJ_DEF(pj_status_t) pj_init(void)
+{
+ WSADATA wsa;
+ char dummy_guid[32]; /* use maximum GUID length */
+ pj_str_t guid;
+ pj_status_t rc;
+
+ PJ_LOG(5, ("pj_init", "Initializing PJ Library.."));
+
+ /* Init Winsock.. */
+ if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) {
+ PJ_LOG(1, ("pj_init", "Winsock initialization has returned an error"));
+ return PJ_RETURN_OS_ERROR(WSAGetLastError());
+ }
+
+ /* Init this thread's TLS. */
+ if ((rc=pj_thread_init()) != PJ_SUCCESS) {
+ PJ_LOG(1, ("pj_init", "Thread initialization has returned an error"));
+ return rc;
+ }
+
+ /* Init random seed. */
+ pj_srand( GetCurrentProcessId() );
+
+ /* Startup GUID. */
+ guid.ptr = dummy_guid;
+ pj_generate_unique_string( &guid );
+
+ /* Initialize critical section. */
+ if ((rc=init_mutex(&critical_section_mutex, "pj%p")) != PJ_SUCCESS)
+ return rc;
+
+ /* Initialize exception ID for the pool.
+ * Must do so after critical section is configured.
+ */
+ rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ /* Startup timestamp */
+#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
+ {
+ pj_timestamp dummy_ts;
+ if ((rc=pj_get_timestamp(&dummy_ts)) != PJ_SUCCESS) {
+ PJ_LOG(1, ("pj_init", "Unable to initialize timestamp"));
+ return rc;
+ }
+ }
+#endif
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_getpid(void)
+ */
+PJ_DEF(pj_uint32_t) pj_getpid(void)
+{
+ PJ_CHECK_STACK();
+ return GetCurrentProcessId();
+}
+
+/*
+ * pj_thread_register(..)
+ */
+PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name,
+ pj_thread_desc desc,
+ pj_thread_t **thread_ptr)
+{
+ char stack_ptr;
+ pj_thread_t *thread = (pj_thread_t *)desc;
+ pj_str_t thread_name = pj_str((char*)cstr_thread_name);
+
+ /* Size sanity check. */
+ if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) {
+ pj_assert(!"Not enough pj_thread_desc size!");
+ return PJ_EBUG;
+ }
+
+ /* If a thread descriptor has been registered before, just return it. */
+ if (pj_thread_local_get (thread_tls_id) != 0) {
+ *thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id);
+ return PJ_SUCCESS;
+ }
+
+ /* Initialize and set the thread entry. */
+ pj_memset(desc, 0, sizeof(pj_thread_desc));
+ thread->hthread = GetCurrentThread();
+ thread->idthread = GetCurrentThreadId();
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+ thread->stk_start = &stack_ptr;
+ thread->stk_size = 0xFFFFFFFFUL;
+ thread->stk_max_usage = 0;
+#else
+ stack_ptr = '\0';
+#endif
+
+ if (cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1)
+ pj_sprintf(thread->obj_name, cstr_thread_name, thread->idthread);
+ else
+ pj_sprintf(thread->obj_name, "thr%p", (void*)thread->idthread);
+
+ pj_thread_local_set(thread_tls_id, thread);
+
+ *thread_ptr = thread;
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_thread_init(void)
+ */
+pj_status_t pj_thread_init(void)
+{
+ pj_status_t rc;
+ pj_thread_t *thread;
+
+ rc = pj_thread_local_alloc(&thread_tls_id);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ return pj_thread_register("thr%p", main_thread, &thread);
+}
+
+static DWORD WINAPI thread_main(void *param)
+{
+ pj_thread_t *rec = param;
+ DWORD result;
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+ rec->stk_start = (char*)&rec;
+#endif
+
+ PJ_LOG(6,(rec->obj_name, "Thread started"));
+
+ pj_thread_local_set(thread_tls_id, rec);
+ result = (*rec->proc)(rec->arg);
+
+ PJ_LOG(6,(rec->obj_name, "Thread quitting"));
+ return (DWORD)result;
+}
+
+/*
+ * pj_thread_create(...)
+ */
+PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool,
+ const char *thread_name,
+ pj_thread_proc *proc,
+ void *arg,
+ pj_size_t stack_size,
+ unsigned flags,
+ pj_thread_t **thread_ptr)
+{
+ DWORD dwflags = 0;
+ pj_thread_t *rec;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(pool && proc && thread_ptr, PJ_EINVAL);
+
+ /* Set flags */
+ if (flags & PJ_THREAD_SUSPENDED)
+ dwflags |= CREATE_SUSPENDED;
+
+ /* Create thread record and assign name for the thread */
+ rec = (struct pj_thread_t*) pj_pool_calloc(pool, 1, sizeof(pj_thread_t));
+ if (!rec)
+ return PJ_ENOMEM;
+
+ /* Set name. */
+ if (!thread_name)
+ thread_name = "thr%p";
+
+ if (strchr(thread_name, '%')) {
+ pj_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec);
+ } else {
+ strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME);
+ rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+ }
+
+ PJ_LOG(6, (rec->obj_name, "Thread created"));
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+ rec->stk_size = stack_size ? stack_size : 0xFFFFFFFFUL;
+ rec->stk_max_usage = 0;
+#endif
+
+ /* Create the thread. */
+ rec->proc = proc;
+ rec->arg = arg;
+ rec->hthread = CreateThread(NULL, stack_size,
+ thread_main, rec,
+ dwflags, &rec->idthread);
+ if (rec->hthread == NULL)
+ return PJ_RETURN_OS_ERROR(GetLastError());
+
+ /* Success! */
+ *thread_ptr = rec;
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_thread-get_name()
+ */
+PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p)
+{
+ pj_thread_t *rec = (pj_thread_t*)p;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(p, "");
+
+ return rec->obj_name;
+}
+
+/*
+ * pj_thread_resume()
+ */
+PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p)
+{
+ pj_thread_t *rec = (pj_thread_t*)p;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(p, PJ_EINVAL);
+
+ if (ResumeThread(rec->hthread) == (DWORD)-1)
+ return PJ_RETURN_OS_ERROR(GetLastError());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_thread_this()
+ */
+PJ_DEF(pj_thread_t*) pj_thread_this(void)
+{
+ pj_thread_t *rec = pj_thread_local_get(thread_tls_id);
+ pj_assert(rec != NULL);
+
+ /*
+ * MUST NOT check stack because this function is called
+ * by PJ_CHECK_STACK() itself!!!
+ *
+ */
+
+ return rec;
+}
+
+/*
+ * pj_thread_join()
+ */
+PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p)
+{
+ pj_thread_t *rec = (pj_thread_t *)p;
+ DWORD rc;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(p, PJ_EINVAL);
+
+ PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name));
+
+ rc = WaitForSingleObject(rec->hthread, INFINITE);
+
+ if (rc==WAIT_OBJECT_0)
+ return PJ_SUCCESS;
+ else if (rc==WAIT_TIMEOUT)
+ return PJ_ETIMEDOUT;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_thread_destroy()
+ */
+PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p)
+{
+ pj_thread_t *rec = (pj_thread_t *)p;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(p, PJ_EINVAL);
+
+ if (CloseHandle(rec->hthread) == TRUE)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_thread_sleep()
+ */
+PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
+{
+ PJ_CHECK_STACK();
+ Sleep(msec);
+ return PJ_SUCCESS;
+}
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0
+/*
+ * pj_thread_check_stack()
+ * Implementation for PJ_CHECK_STACK()
+ */
+PJ_DEF(void) pj_thread_check_stack(const char *file, int line)
+{
+ char stk_ptr;
+ pj_uint32_t usage;
+ pj_thread_t *thread = pj_thread_this();
+
+ pj_assert(thread);
+
+ /* Calculate current usage. */
+ usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start :
+ thread->stk_start - &stk_ptr;
+
+ /* Assert if stack usage is dangerously high. */
+ pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128));
+
+ /* Keep statistic. */
+ if (usage > thread->stk_max_usage) {
+ thread->stk_max_usage = usage;
+ thread->caller_file = file;
+ thread->caller_line = line;
+ }
+
+}
+
+/*
+ * pj_thread_get_stack_max_usage()
+ */
+PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread)
+{
+ return thread->stk_max_usage;
+}
+
+/*
+ * pj_thread_get_stack_info()
+ */
+PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread,
+ const char **file,
+ int *line )
+{
+ pj_assert(thread);
+
+ *file = thread->caller_file;
+ *line = thread->caller_line;
+ return 0;
+}
+
+#endif /* PJ_OS_HAS_CHECK_STACK */
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * pj_atomic_create()
+ */
+PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool,
+ pj_atomic_value_t initial,
+ pj_atomic_t **atomic_ptr)
+{
+ pj_atomic_t *atomic_var = pj_pool_alloc(pool, sizeof(pj_atomic_t));
+ if (!atomic_var)
+ return PJ_ENOMEM;
+
+ atomic_var->value = initial;
+ *atomic_ptr = atomic_var;
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_atomic_destroy()
+ */
+PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var )
+{
+ PJ_UNUSED_ARG(var);
+ PJ_ASSERT_RETURN(var, PJ_EINVAL);
+
+ return 0;
+}
+
+/*
+ * pj_atomic_set()
+ */
+PJ_DEF(long) pj_atomic_set(pj_atomic_t *atomic_var, long value)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(atomic_var, 0);
+
+ return InterlockedExchange(&atomic_var->value, value);
+}
+
+/*
+ * pj_atomic_get()
+ */
+PJ_DEF(long) pj_atomic_get(pj_atomic_t *atomic_var)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(atomic_var, 0);
+
+ return atomic_var->value;
+}
+
+/*
+ * pj_atomic_inc()
+ */
+PJ_DEF(long) pj_atomic_inc(pj_atomic_t *atomic_var)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(atomic_var, 0);
+
+#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
+ return InterlockedIncrement(&atomic_var->value);
+#else
+# error Fix Me
+#endif
+}
+
+/*
+ * pj_atomic_dec()
+ */
+PJ_DEF(long) pj_atomic_dec(pj_atomic_t *atomic_var)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(atomic_var, 0);
+
+#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
+ return InterlockedDecrement(&atomic_var->value);
+#else
+# error Fix me
+#endif
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * pj_thread_local_alloc()
+ */
+PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index)
+{
+ PJ_ASSERT_RETURN(index != NULL, PJ_EINVAL);
+
+ //Can't check stack because this function is called in the
+ //beginning before main thread is initialized.
+ //PJ_CHECK_STACK();
+
+ *index = TlsAlloc();
+
+ if (*index == TLS_OUT_OF_INDEXES)
+ return PJ_RETURN_OS_ERROR(GetLastError());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_thread_local_free()
+ */
+PJ_DEF(void) pj_thread_local_free(long index)
+{
+ PJ_CHECK_STACK();
+ TlsFree(index);
+}
+
+/*
+ * pj_thread_local_set()
+ */
+PJ_DEF(void) pj_thread_local_set(long index, void *value)
+{
+ //Can't check stack because this function is called in the
+ //beginning before main thread is initialized.
+ //PJ_CHECK_STACK();
+ TlsSetValue(index, value);
+}
+
+/*
+ * pj_thread_local_get()
+ */
+PJ_DEF(void*) pj_thread_local_get(long index)
+{
+ //Can't check stack because this function is called
+ //by PJ_CHECK_STACK() itself!!!
+ //PJ_CHECK_STACK();
+ return TlsGetValue(index);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name)
+{
+
+ PJ_CHECK_STACK();
+
+#if PJ_WIN32_WINNT >= 0x0400
+ InitializeCriticalSection(&mutex->crit);
+#else
+ mutex->hMutex = CreateMutex(NULL, FALSE, NULL);
+ if (!mutex->hMutex) {
+ return PJ_RETURN_OS_ERROR(GetLastError());
+ }
+#endif
+
+#if PJ_DEBUG
+ /* Set owner. */
+ mutex->nesting_level = 0;
+ mutex->owner = NULL;
+#endif
+
+ /* Set name. */
+ if (!name) {
+ name = "mtx%p";
+ }
+ if (strchr(name, '%')) {
+ pj_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex);
+ } else {
+ strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME);
+ mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+ }
+
+ PJ_LOG(6, (mutex->obj_name, "Mutex created"));
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_mutex_create()
+ */
+PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool,
+ const char *name,
+ int type,
+ pj_mutex_t **mutex_ptr)
+{
+ pj_status_t rc;
+ pj_mutex_t *mutex;
+
+ PJ_UNUSED_ARG(type);
+ PJ_ASSERT_RETURN(pool && mutex_ptr, PJ_EINVAL);
+
+ mutex = pj_pool_alloc(pool, sizeof(*mutex));
+ if (!mutex)
+ return PJ_ENOMEM;
+
+ rc = init_mutex(mutex, name);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ *mutex_ptr = mutex;
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_mutex_create_simple()
+ */
+PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool,
+ const char *name,
+ pj_mutex_t **mutex )
+{
+ return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex);
+}
+
+/*
+ * pj_mutex_create_recursive()
+ */
+PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
+ const char *name,
+ pj_mutex_t **mutex )
+{
+ return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex);
+}
+
+/*
+ * pj_mutex_lock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
+{
+ pj_status_t status;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+ PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting",
+ pj_thread_this()->obj_name));
+
+#if PJ_WIN32_WINNT >= 0x0400
+ EnterCriticalSection(&mutex->crit);
+ status=PJ_SUCCESS;
+#else
+ if (WaitForSingleObject(mutex->hMutex, INFINITE)==WAIT_OBJECT_0)
+ status = PJ_SUCCESS;
+ else
+ status = PJ_STATUS_FROM_OS(GetLastError());
+
+#endif
+ PJ_LOG(6,(mutex->obj_name,
+ (status==PJ_SUCCESS ? "Mutex acquired by thread %s" : "FAILED by %s"),
+ pj_thread_this()->obj_name));
+
+#if PJ_DEBUG
+ if (status == PJ_SUCCESS) {
+ mutex->owner = pj_thread_this();
+ ++mutex->nesting_level;
+ }
+#endif
+
+ return status;
+}
+
+/*
+ * pj_mutex_unlock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
+{
+ pj_status_t status;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+#if PJ_DEBUG
+ pj_assert(mutex->owner == pj_thread_this());
+ if (--mutex->nesting_level == 0) {
+ mutex->owner = NULL;
+ }
+#endif
+
+ PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s",
+ pj_thread_this()->obj_name));
+
+#if PJ_WIN32_WINNT >= 0x0400
+ LeaveCriticalSection(&mutex->crit);
+ status=PJ_SUCCESS;
+#else
+ status = ReleaseMutex(mutex->hMutex) ? PJ_SUCCESS :
+ PJ_STATUS_FROM_OS(GetLastError());
+#endif
+ return status;
+}
+
+/*
+ * pj_mutex_trylock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
+{
+ pj_status_t status;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+#if PJ_WIN32_WINNT >= 0x0400
+ status=TryEnterCriticalSection(&mutex->crit) ? PJ_SUCCESS : PJ_EUNKNOWN;
+#else
+ status = WaitForSingleObject(mutex->hMutex, 0)==WAIT_OBJECT_0 ?
+ PJ_SUCCESS : PJ_ETIMEDOUT;
+#endif
+ if (status==PJ_SUCCESS) {
+ PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s",
+ pj_thread_this()->obj_name));
+
+#if PJ_DEBUG
+ mutex->owner = pj_thread_this();
+ ++mutex->nesting_level;
+#endif
+ }
+ return status;
+}
+
+/*
+ * pj_mutex_destroy()
+ */
+PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+ PJ_LOG(6,(mutex->obj_name, "Mutex destroyed"));
+
+#if PJ_WIN32_WINNT >= 0x0400
+ DeleteCriticalSection(&mutex->crit);
+ return PJ_SUCCESS;
+#else
+ return CloseHandle(mutex->hMutex) ? PJ_SUCCESS :
+ PJ_RETURN_OS_ERROR(GetLastError());
+#endif
+}
+
+#if PJ_DEBUG
+/*
+ * pj_mutex_is_locked()
+ */
+PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex)
+{
+ return mutex->owner == pj_thread_this();
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * pj_enter_critical_section()
+ */
+PJ_DEF(void) pj_enter_critical_section(void)
+{
+ pj_mutex_lock(&critical_section_mutex);
+}
+
+
+/*
+ * pj_leave_critical_section()
+ */
+PJ_DEF(void) pj_leave_critical_section(void)
+{
+ pj_mutex_unlock(&critical_section_mutex);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+
+/*
+ * pj_sem_create()
+ */
+PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool,
+ const char *name,
+ unsigned initial,
+ unsigned max,
+ pj_sem_t **sem_ptr)
+{
+ pj_sem_t *sem;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(pool && sem_ptr, PJ_EINVAL);
+
+ sem = pj_pool_alloc(pool, sizeof(*sem));
+ sem->hSemaphore = CreateSemaphore(NULL, initial, max, NULL);
+ if (!sem->hSemaphore)
+ return PJ_RETURN_OS_ERROR(GetLastError());
+
+ /* Set name. */
+ if (!name) {
+ name = "sem%p";
+ }
+ if (strchr(name, '%')) {
+ pj_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem);
+ } else {
+ strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME);
+ sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+ }
+
+ PJ_LOG(6, (sem->obj_name, "Semaphore created"));
+
+ *sem_ptr = sem;
+ return PJ_SUCCESS;
+}
+
+static pj_status_t pj_sem_wait_for(pj_sem_t *sem, unsigned timeout)
+{
+ DWORD result;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+ PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting",
+ pj_thread_this()->obj_name));
+
+ result = WaitForSingleObject(sem->hSemaphore, timeout);
+ if (result == WAIT_OBJECT_0) {
+ PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s",
+ pj_thread_this()->obj_name));
+ } else {
+ PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire",
+ pj_thread_this()->obj_name));
+ }
+
+ if (result==WAIT_OBJECT_0)
+ return PJ_SUCCESS;
+ else if (result==WAIT_TIMEOUT)
+ return PJ_ETIMEDOUT;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_sem_wait()
+ */
+PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+ return pj_sem_wait_for(sem, INFINITE);
+}
+
+/*
+ * pj_sem_trywait()
+ */
+PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+ return pj_sem_wait_for(sem, 0);
+}
+
+/*
+ * pj_sem_post()
+ */
+PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+ PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s",
+ pj_thread_this()->obj_name));
+
+ if (ReleaseSemaphore(sem->hSemaphore, 1, NULL))
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_sem_destroy()
+ */
+PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+ PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s",
+ pj_thread_this()->obj_name));
+
+ if (CloseHandle(sem->hSemaphore))
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+#endif /* PJ_HAS_SEMAPHORE */
+///////////////////////////////////////////////////////////////////////////////
+
+
+#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
+
+/*
+ * pj_event_create()
+ */
+PJ_DEF(pj_status_t) pj_event_create( pj_pool_t *pool,
+ const char *name,
+ pj_bool_t manual_reset,
+ pj_bool_t initial,
+ pj_event_t **event_ptr)
+{
+ pj_event_t *event;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(pool && event_ptr, PJ_EINVAL);
+
+ event = pj_pool_alloc(pool, sizeof(*event));
+ if (!event)
+ return PJ_ENOMEM;
+
+ event->hEvent = CreateEvent(NULL, manual_reset?TRUE:FALSE,
+ initial?TRUE:FALSE, NULL);
+
+ if (!event->hEvent)
+ return PJ_RETURN_OS_ERROR(GetLastError());
+
+ /* Set name. */
+ if (!name) {
+ name = "evt%p";
+ }
+ if (strchr(name, '%')) {
+ pj_snprintf(event->obj_name, PJ_MAX_OBJ_NAME, name, event);
+ } else {
+ strncpy(event->obj_name, name, PJ_MAX_OBJ_NAME);
+ event->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+ }
+
+ PJ_LOG(6, (event->obj_name, "Event created"));
+
+ *event_ptr = event;
+ return PJ_SUCCESS;
+}
+
+static pj_status_t pj_event_wait_for(pj_event_t *event, unsigned timeout)
+{
+ DWORD result;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+ PJ_LOG(6, (event->obj_name, "Event: thread %s is waiting",
+ pj_thread_this()->obj_name));
+
+ result = WaitForSingleObject(event->hEvent, timeout);
+ if (result == WAIT_OBJECT_0) {
+ PJ_LOG(6, (event->obj_name, "Event: thread %s is released",
+ pj_thread_this()->obj_name));
+ } else {
+ PJ_LOG(6, (event->obj_name, "Event: thread %s FAILED to acquire",
+ pj_thread_this()->obj_name));
+ }
+
+ if (result==WAIT_OBJECT_0)
+ return PJ_SUCCESS;
+ else if (result==WAIT_TIMEOUT)
+ return PJ_ETIMEDOUT;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_event_wait()
+ */
+PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event)
+{
+ PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+ return pj_event_wait_for(event, INFINITE);
+}
+
+/*
+ * pj_event_trywait()
+ */
+PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event)
+{
+ PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+ return pj_event_wait_for(event, 0);
+}
+
+/*
+ * pj_event_set()
+ */
+PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+ PJ_LOG(6, (event->obj_name, "Setting event"));
+
+ if (SetEvent(event->hEvent))
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_event_pulse()
+ */
+PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+ PJ_LOG(6, (event->obj_name, "Pulsing event"));
+
+ if (PulseEvent(event->hEvent))
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_event_reset()
+ */
+PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+ PJ_LOG(6, (event->obj_name, "Event is reset"));
+
+ if (ResetEvent(event->hEvent))
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_event_destroy()
+ */
+PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+ PJ_LOG(6, (event->obj_name, "Event is destroying"));
+
+ if (CloseHandle(event->hEvent))
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+#endif /* PJ_HAS_EVENT_OBJ */
+
+///////////////////////////////////////////////////////////////////////////////
+#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
+/*
+ * Terminal color
+ */
+
+static WORD pj_color_to_os_attr(pj_color_t color)
+{
+ WORD attr = 0;
+
+ if (color & PJ_TERM_COLOR_R)
+ attr |= FOREGROUND_RED;
+ if (color & PJ_TERM_COLOR_G)
+ attr |= FOREGROUND_GREEN;
+ if (color & PJ_TERM_COLOR_B)
+ attr |= FOREGROUND_BLUE;
+ if (color & PJ_TERM_COLOR_BRIGHT)
+ attr |= FOREGROUND_INTENSITY;
+
+ return attr;
+}
+
+static pj_color_t os_attr_to_pj_color(WORD attr)
+{
+ int color = 0;
+
+ if (attr & FOREGROUND_RED)
+ color |= PJ_TERM_COLOR_R;
+ if (attr & FOREGROUND_GREEN)
+ color |= PJ_TERM_COLOR_G;
+ if (attr & FOREGROUND_BLUE)
+ color |= PJ_TERM_COLOR_B;
+ if (attr & FOREGROUND_INTENSITY)
+ color |= PJ_TERM_COLOR_BRIGHT;
+
+ return color;
+}
+
+
+/*
+ * pj_term_set_color()
+ */
+PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color)
+{
+ BOOL rc;
+ WORD attr = 0;
+
+ PJ_CHECK_STACK();
+
+ attr = pj_color_to_os_attr(color);
+ rc = SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), attr);
+ return rc ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_term_get_color()
+ * Get current terminal foreground color.
+ */
+PJ_DEF(pj_color_t) pj_term_get_color(void)
+{
+ CONSOLE_SCREEN_BUFFER_INFO info;
+
+ PJ_CHECK_STACK();
+
+ GetConsoleScreenBufferInfo( GetStdHandle(STD_OUTPUT_HANDLE), &info);
+ return os_attr_to_pj_color(info.wAttributes);
+}
+
+#endif /* PJ_TERM_HAS_COLOR */
diff --git a/pjlib/src/pj/os_error_linux_kernel.c b/pjlib/src/pj/os_error_linux_kernel.c
new file mode 100644
index 00000000..4c83b491
--- /dev/null
+++ b/pjlib/src/pj/os_error_linux_kernel.c
@@ -0,0 +1,73 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/os_error_linux_kernel.c 2 10/29/05 11:51a Bennylp $ */
+/*
+ * $Log: /pjproject-0.3/pjlib/src/pj/os_error_linux_kernel.c $
+ *
+ * 2 10/29/05 11:51a Bennylp
+ * Version 0.3-pre2.
+ *
+ * 1 10/19/05 1:48p Bennylp
+ * Created.
+ *
+ */
+#include <pj/string.h>
+#include <pj/compat/errno.h>
+#include <linux/config.h>
+#include <linux/version.h>
+#if defined(MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+int kernel_errno;
+
+PJ_DEF(pj_status_t) pj_get_os_error(void)
+{
+ return errno;
+}
+
+PJ_DEF(void) pj_set_os_error(pj_status_t code)
+{
+ errno = code;
+}
+
+PJ_DEF(pj_status_t) pj_get_netos_error(void)
+{
+ return errno;
+}
+
+PJ_DEF(void) pj_set_netos_error(pj_status_t code)
+{
+ errno = code;
+}
+
+/*
+ * platform_strerror()
+ *
+ * Platform specific error message. This file is called by pj_strerror()
+ * in errno.c
+ */
+int platform_strerror( pj_os_err_type os_errcode,
+ char *buf, pj_size_t bufsize)
+{
+ char errmsg[32];
+ int len;
+
+ /* Handle EINVAL as special case so that it'll pass errno test. */
+ if (os_errcode==EINVAL)
+ strcpy(errmsg, "Invalid value");
+ else
+ sprintf(errmsg, "errno=%d", os_errcode);
+
+ len = strlen(errmsg);
+
+ if (len >= bufsize)
+ len = bufsize-1;
+
+ pj_memcpy(buf, errmsg, len);
+ buf[len] = '\0';
+
+ return len;
+}
+
+
diff --git a/pjlib/src/pj/os_error_unix.c b/pjlib/src/pj/os_error_unix.c
new file mode 100644
index 00000000..f526c671
--- /dev/null
+++ b/pjlib/src/pj/os_error_unix.c
@@ -0,0 +1,52 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/os_error_unix.c 1 10/14/05 12:19a Bennylp $ */
+/*
+ * $Log: /pjproject-0.3/pjlib/src/pj/os_error_unix.c $
+ *
+ * 1 10/14/05 12:19a Bennylp
+ * Created.
+ *
+ */
+#include <pj/errno.h>
+#include <pj/string.h>
+#include <errno.h>
+
+PJ_DEF(pj_status_t) pj_get_os_error(void)
+{
+ return PJ_STATUS_FROM_OS(errno);
+}
+
+PJ_DEF(void) pj_set_os_error(pj_status_t code)
+{
+ errno = PJ_STATUS_TO_OS(code);
+}
+
+PJ_DEF(pj_status_t) pj_get_netos_error(void)
+{
+ return PJ_STATUS_FROM_OS(errno);
+}
+
+PJ_DEF(void) pj_set_netos_error(pj_status_t code)
+{
+ errno = PJ_STATUS_TO_OS(code);
+}
+
+/*
+ * platform_strerror()
+ *
+ * Platform specific error message. This file is called by pj_strerror()
+ * in errno.c
+ */
+int platform_strerror( pj_os_err_type os_errcode,
+ char *buf, pj_size_t bufsize)
+{
+ const char *syserr = strerror(os_errcode);
+ pj_size_t len = syserr ? strlen(syserr) : 0;
+
+ if (len >= bufsize) len = bufsize - 1;
+ if (len > 0)
+ pj_memcpy(buf, syserr, len);
+ buf[len] = '\0';
+ return len;
+}
+
+
diff --git a/pjlib/src/pj/os_error_win32.c b/pjlib/src/pj/os_error_win32.c
new file mode 100644
index 00000000..19471fcf
--- /dev/null
+++ b/pjlib/src/pj/os_error_win32.c
@@ -0,0 +1,161 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/os_error_win32.c 3 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/os_error_win32.c $
+ *
+ * 3 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 2 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 1 9/17/05 10:36a Bennylp
+ * Created.
+ *
+ */
+#include <pj/errno.h>
+#include <pj/assert.h>
+#include <pj/compat/stdarg.h>
+#include <pj/compat/sprintf.h>
+#include <pj/compat/vsprintf.h>
+#include <pj/string.h>
+
+
+#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0
+# include <winsock2.h>
+#elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0
+# include <winsock.h>
+#endif
+
+
+/*
+ * From Apache's APR:
+ */
+static const struct {
+ pj_os_err_type code;
+ const char *msg;
+} gaErrorList[] = {
+ {WSAEINTR, "Interrupted system call"},
+ {WSAEBADF, "Bad file number"},
+ {WSAEACCES, "Permission denied"},
+ {WSAEFAULT, "Bad address"},
+ {WSAEINVAL, "Invalid argument"},
+ {WSAEMFILE, "Too many open sockets"},
+ {WSAEWOULDBLOCK, "Operation would block"},
+ {WSAEINPROGRESS, "Operation now in progress"},
+ {WSAEALREADY, "Operation already in progress"},
+ {WSAENOTSOCK, "Socket operation on non-socket"},
+ {WSAEDESTADDRREQ, "Destination address required"},
+ {WSAEMSGSIZE, "Message too long"},
+ {WSAEPROTOTYPE, "Protocol wrong type for socket"},
+ {WSAENOPROTOOPT, "Bad protocol option"},
+ {WSAEPROTONOSUPPORT, "Protocol not supported"},
+ {WSAESOCKTNOSUPPORT, "Socket type not supported"},
+ {WSAEOPNOTSUPP, "Operation not supported on socket"},
+ {WSAEPFNOSUPPORT, "Protocol family not supported"},
+ {WSAEAFNOSUPPORT, "Address family not supported"},
+ {WSAEADDRINUSE, "Address already in use"},
+ {WSAEADDRNOTAVAIL, "Can't assign requested address"},
+ {WSAENETDOWN, "Network is down"},
+ {WSAENETUNREACH, "Network is unreachable"},
+ {WSAENETRESET, "Net connection reset"},
+ {WSAECONNABORTED, "Software caused connection abort"},
+ {WSAECONNRESET, "Connection reset by peer"},
+ {WSAENOBUFS, "No buffer space available"},
+ {WSAEISCONN, "Socket is already connected"},
+ {WSAENOTCONN, "Socket is not connected"},
+ {WSAESHUTDOWN, "Can't send after socket shutdown"},
+ {WSAETOOMANYREFS, "Too many references, can't splice"},
+ {WSAETIMEDOUT, "Connection timed out"},
+ {WSAECONNREFUSED, "Connection refused"},
+ {WSAELOOP, "Too many levels of symbolic links"},
+ {WSAENAMETOOLONG, "File name too long"},
+ {WSAEHOSTDOWN, "Host is down"},
+ {WSAEHOSTUNREACH, "No route to host"},
+ {WSAENOTEMPTY, "Directory not empty"},
+ {WSAEPROCLIM, "Too many processes"},
+ {WSAEUSERS, "Too many users"},
+ {WSAEDQUOT, "Disc quota exceeded"},
+ {WSAESTALE, "Stale NFS file handle"},
+ {WSAEREMOTE, "Too many levels of remote in path"},
+ {WSASYSNOTREADY, "Network system is unavailable"},
+ {WSAVERNOTSUPPORTED, "Winsock version out of range"},
+ {WSANOTINITIALISED, "WSAStartup not yet called"},
+ {WSAEDISCON, "Graceful shutdown in progress"},
+ {WSAHOST_NOT_FOUND, "Host not found"},
+ {WSANO_DATA, "No host data of that type was found"},
+ {0, NULL}
+};
+
+
+PJ_DEF(pj_status_t) pj_get_os_error(void)
+{
+ return PJ_STATUS_FROM_OS(GetLastError());
+}
+
+PJ_DEF(void) pj_set_os_error(pj_status_t code)
+{
+ SetLastError(PJ_STATUS_TO_OS(code));
+}
+
+PJ_DEF(pj_status_t) pj_get_netos_error(void)
+{
+ return PJ_STATUS_FROM_OS(WSAGetLastError());
+}
+
+PJ_DEF(void) pj_set_netos_error(pj_status_t code)
+{
+ WSASetLastError(PJ_STATUS_TO_OS(code));
+}
+
+/*
+ * platform_strerror()
+ *
+ * Platform specific error message. This file is called by pj_strerror()
+ * in errno.c
+ */
+int platform_strerror( pj_os_err_type os_errcode,
+ char *buf, pj_size_t bufsize)
+{
+ int len;
+
+ pj_assert(buf != NULL);
+ pj_assert(bufsize >= 0);
+
+ /*
+ * MUST NOT check stack here.
+ * This function might be called from PJ_CHECK_STACK() itself!
+ //PJ_CHECK_STACK();
+ */
+
+ len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ os_errcode,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR)buf,
+ (DWORD)bufsize,
+ NULL);
+
+ if (!len) {
+ int i;
+ for (i = 0; gaErrorList[i].msg; ++i) {
+ if (gaErrorList[i].code == os_errcode) {
+ len = strlen(gaErrorList[i].msg);
+ if ((pj_size_t)len >= bufsize) {
+ len = bufsize-1;
+ }
+ pj_memcpy(buf, gaErrorList[i].msg, len);
+ buf[len] = '\0';
+ break;
+ }
+ }
+ }
+
+ if (!len) {
+ len = snprintf( buf, bufsize, "Unknown native error %u", (unsigned)os_errcode);
+ buf[len] = '\0';
+ }
+
+ return len;
+}
+
diff --git a/pjlib/src/pj/os_time_ansi.c b/pjlib/src/pj/os_time_ansi.c
new file mode 100644
index 00000000..906b21d6
--- /dev/null
+++ b/pjlib/src/pj/os_time_ansi.c
@@ -0,0 +1,65 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/os_time_ansi.c 2 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/os_time_ansi.c $
+ *
+ * 2 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 1 9/17/05 10:36a Bennylp
+ * Created.
+ *
+ */
+#include <pj/os.h>
+#include <pj/compat/time.h>
+
+///////////////////////////////////////////////////////////////////////////////
+
+PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv)
+{
+ struct timeb tb;
+
+ PJ_CHECK_STACK();
+
+ ftime(&tb);
+ tv->sec = tb.time;
+ tv->msec = tb.millitm;
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt)
+{
+ struct tm *local_time;
+
+ PJ_CHECK_STACK();
+
+ local_time = localtime((time_t*)&tv->sec);
+
+ pt->year = local_time->tm_year+1900;
+ pt->mon = local_time->tm_mon;
+ pt->day = local_time->tm_mday;
+ pt->hour = local_time->tm_hour;
+ pt->min = local_time->tm_min;
+ pt->sec = local_time->tm_sec;
+ pt->wday = local_time->tm_wday;
+ pt->yday = local_time->tm_yday;
+ pt->msec = tv->msec;
+
+ return PJ_SUCCESS;
+}
+
+/**
+ * Encode parsed time to time value.
+ */
+PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv);
+
+/**
+ * Convert local time to GMT.
+ */
+PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv);
+
+/**
+ * Convert GMT to local time.
+ */
+PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv);
+
+
diff --git a/pjlib/src/pj/os_time_linux_kernel.c b/pjlib/src/pj/os_time_linux_kernel.c
new file mode 100644
index 00000000..4d5f4cb4
--- /dev/null
+++ b/pjlib/src/pj/os_time_linux_kernel.c
@@ -0,0 +1,58 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/os_time_linux_kernel.c 2 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/os_time_linux_kernel.c $
+ *
+ * 2 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 1 9/22/05 10:39a Bennylp
+ * Created.
+ *
+ */
+#include <pj/os.h>
+#include <linux/time.h>
+
+///////////////////////////////////////////////////////////////////////////////
+
+PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv)
+{
+ struct timeval tval;
+
+ do_gettimeofday(&tval);
+ tv->sec = tval.tv_sec;
+ tv->msec = tval.tv_usec / 1000;
+
+ return 0;
+}
+
+PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt)
+{
+ pt->year = 2005;
+ pt->mon = 8;
+ pt->day = 20;
+ pt->hour = 16;
+ pt->min = 30;
+ pt->sec = 30;
+ pt->wday = 3;
+ pt->yday = 200;
+ pt->msec = 777;
+
+ return -1;
+}
+
+/**
+ * Encode parsed time to time value.
+ */
+PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv);
+
+/**
+ * Convert local time to GMT.
+ */
+PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv);
+
+/**
+ * Convert GMT to local time.
+ */
+PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv);
+
+
diff --git a/pjlib/src/pj/os_timestamp_common.c b/pjlib/src/pj/os_timestamp_common.c
new file mode 100644
index 00000000..630ffb27
--- /dev/null
+++ b/pjlib/src/pj/os_timestamp_common.c
@@ -0,0 +1,129 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/os_timestamp_common.c 2 10/14/05 12:26a Bennylp $ */
+/*
+ * $Log: /pjproject-0.3/pjlib/src/pj/os_timestamp_common.c $
+ *
+ * 2 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 1 10/09/05 2:56p Bennylp
+ * Created.
+ *
+ */
+#include <pj/os.h>
+#include <pj/compat/high_precision.h>
+
+#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
+
+#define U32MAX (0xFFFFFFFFUL)
+#define NANOSEC (1000000000UL)
+#define USEC (1000000UL)
+#define MSEC (1000)
+
+static pj_highprec_t get_elapsed( const pj_timestamp *start,
+ const pj_timestamp *stop )
+{
+ pj_highprec_t elapsed_hi, elapsed_lo;
+
+ elapsed_hi = stop->u32.hi - start->u32.hi;
+ elapsed_lo = stop->u32.lo - start->u32.lo;
+
+ /* elapsed_hi = elapsed_hi * U32MAX */
+ pj_highprec_mul(elapsed_hi, U32MAX);
+
+ return elapsed_hi + elapsed_lo;
+}
+
+static pj_highprec_t elapsed_usec( const pj_timestamp *start,
+ const pj_timestamp *stop )
+{
+ pj_timestamp ts_freq;
+ pj_highprec_t freq, elapsed;
+
+ if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS)
+ return 0;
+
+ /* Convert frequency timestamp */
+ freq = ts_freq.u32.hi;
+ pj_highprec_mul(freq, U32MAX);
+ freq += ts_freq.u32.lo;
+
+ /* Avoid division by zero. */
+ if (freq == 0) freq = 1;
+
+ /* Get elapsed time in cycles. */
+ elapsed = get_elapsed(start, stop);
+
+ /* usec = elapsed * USEC / freq */
+ pj_highprec_mul(elapsed, USEC);
+ pj_highprec_div(elapsed, freq);
+
+ return elapsed;
+}
+
+PJ_DEF(pj_uint32_t) pj_elapsed_nanosec( const pj_timestamp *start,
+ const pj_timestamp *stop )
+{
+ pj_timestamp ts_freq;
+ pj_highprec_t freq, elapsed;
+
+ if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS)
+ return 0;
+
+ /* Convert frequency timestamp */
+ freq = ts_freq.u32.hi;
+ pj_highprec_mul(freq, U32MAX);
+ freq += ts_freq.u32.lo;
+
+ /* Avoid division by zero. */
+ if (freq == 0) freq = 1;
+
+ /* Get elapsed time in cycles. */
+ elapsed = get_elapsed(start, stop);
+
+ /* usec = elapsed * USEC / freq */
+ pj_highprec_mul(elapsed, NANOSEC);
+ pj_highprec_div(elapsed, freq);
+
+ return (pj_uint32_t)elapsed;
+}
+
+PJ_DEF(pj_uint32_t) pj_elapsed_usec( const pj_timestamp *start,
+ const pj_timestamp *stop )
+{
+ return (pj_uint32_t)elapsed_usec(start, stop);
+}
+
+PJ_DEF(pj_time_val) pj_elapsed_time( const pj_timestamp *start,
+ const pj_timestamp *stop )
+{
+ pj_highprec_t elapsed = elapsed_usec(start, stop);
+ pj_time_val tv_elapsed;
+
+ if (PJ_HIGHPREC_VALUE_IS_ZERO(elapsed)) {
+ tv_elapsed.sec = tv_elapsed.msec = 0;
+ return tv_elapsed;
+ } else {
+ pj_highprec_t sec, msec;
+
+ sec = elapsed;
+ pj_highprec_div(sec, USEC);
+ tv_elapsed.sec = (long)sec;
+
+ msec = elapsed;
+ pj_highprec_mod(msec, USEC);
+ pj_highprec_div(msec, 1000);
+ tv_elapsed.msec = (long)msec;
+
+ return tv_elapsed;
+ }
+}
+
+PJ_DEF(pj_uint32_t) pj_elapsed_cycle( const pj_timestamp *start,
+ const pj_timestamp *stop )
+{
+ return stop->u32.lo - start->u32.lo;
+}
+
+#endif /* PJ_HAS_HIGH_RES_TIMER */
+
diff --git a/pjlib/src/pj/os_timestamp_linux.c b/pjlib/src/pj/os_timestamp_linux.c
new file mode 100644
index 00000000..52639dcd
--- /dev/null
+++ b/pjlib/src/pj/os_timestamp_linux.c
@@ -0,0 +1,137 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/os_timestamp_linux.c 4 10/29/05 10:27p Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/os_timestamp_linux.c $
+ *
+ * 4 10/29/05 10:27p Bennylp
+ * Fixed misc warnings.
+ *
+ * 3 10/29/05 11:51a Bennylp
+ * Version 0.3-pre2.
+ *
+ * 2 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 1 9/18/05 9:25p Bennylp
+ * Created.
+ *
+ */
+#include <pj/os.h>
+#include <pj/errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#if defined(PJ_HAS_PENTIUM) && PJ_HAS_PENTIUM!=0
+static int machine_speed_mhz;
+static pj_timestamp machine_speed;
+
+static __inline__ unsigned long long int rdtsc()
+{
+ unsigned long long int x;
+ __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
+ return x;
+}
+
+/* Determine machine's CPU MHz to get the counter's frequency.
+ */
+static int get_machine_speed_mhz()
+{
+ FILE *strm;
+ char buf[512];
+ int len;
+ char *pos, *end;
+
+ PJ_CHECK_STACK();
+
+ /* Open /proc/cpuinfo and read the file */
+ strm = fopen("/proc/cpuinfo", "r");
+ if (!strm)
+ return -1;
+ len = fread(buf, 1, sizeof(buf), strm);
+ fclose(strm);
+ if (len < 1) {
+ return -1;
+ }
+ buf[len] = '\0';
+
+ /* Locate the MHz digit. */
+ pos = strstr(buf, "cpu MHz");
+ if (!pos)
+ return -1;
+ pos = strchr(pos, ':');
+ if (!pos)
+ return -1;
+ end = (pos += 2);
+ while (isdigit(*end)) ++end;
+ *end = '\0';
+
+ /* Return the Mhz part, and give it a +1. */
+ return atoi(pos)+1;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+ if (machine_speed_mhz == 0) {
+ machine_speed_mhz = get_machine_speed_mhz();
+ if (machine_speed_mhz > 0) {
+ machine_speed.u64 = machine_speed_mhz * 1000000.0;
+ }
+ }
+
+ if (machine_speed_mhz == -1) {
+ ts->u64 = 0;
+ return -1;
+ }
+ ts->u64 = rdtsc();
+ return 0;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+ if (machine_speed_mhz == 0) {
+ machine_speed_mhz = get_machine_speed_mhz();
+ if (machine_speed_mhz > 0) {
+ machine_speed.u64 = machine_speed_mhz * 1000000.0;
+ }
+ }
+
+ if (machine_speed_mhz == -1) {
+ freq->u64 = 1; /* return 1 to prevent division by zero in apps. */
+ return -1;
+ }
+
+ freq->u64 = machine_speed.u64;
+ return 0;
+}
+
+#else
+#include <sys/time.h>
+#include <errno.h>
+
+#define USEC_PER_SEC 1000000
+
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+ struct timeval tv;
+
+ if (gettimeofday(&tv, NULL) != 0) {
+ return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+ }
+
+ ts->u64 = tv.tv_sec;
+ ts->u64 *= USEC_PER_SEC;
+ ts->u64 += tv.tv_usec;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+ freq->u32.hi = 0;
+ freq->u32.lo = USEC_PER_SEC;
+
+ return PJ_SUCCESS;
+}
+
+#endif
+
diff --git a/pjlib/src/pj/os_timestamp_linux_kernel.c b/pjlib/src/pj/os_timestamp_linux_kernel.c
new file mode 100644
index 00000000..8895cf9d
--- /dev/null
+++ b/pjlib/src/pj/os_timestamp_linux_kernel.c
@@ -0,0 +1,70 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/os_timestamp_linux_kernel.c 2 10/29/05 11:51a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/os_timestamp_linux_kernel.c $
+ *
+ * 2 10/29/05 11:51a Bennylp
+ * Version 0.3-pre2.
+ *
+ * 1 9/22/05 10:39a Bennylp
+ * Created.
+ *
+ */
+#include <pj/os.h>
+#include <linux/time.h>
+
+#if 0
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+ ts->u32.hi = 0;
+ ts->u32.lo = jiffies;
+ return 0;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+ freq->u32.hi = 0;
+ freq->u32.lo = HZ;
+ return 0;
+}
+#elif 0
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+ struct timespec tv;
+
+ tv = CURRENT_TIME;
+
+ ts->u64 = tv.tv_sec;
+ ts->u64 *= NSEC_PER_SEC;
+ ts->u64 += tv.tv_nsec;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+ freq->u32.hi = 0;
+ freq->u32.lo = NSEC_PER_SEC;
+ return 0;
+}
+#else
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+
+ ts->u64 = tv.tv_sec;
+ ts->u64 *= USEC_PER_SEC;
+ ts->u64 += tv.tv_usec;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+ freq->u32.hi = 0;
+ freq->u32.lo = USEC_PER_SEC;
+ return 0;
+}
+
+#endif
+
diff --git a/pjlib/src/pj/os_timestamp_win32.c b/pjlib/src/pj/os_timestamp_win32.c
new file mode 100644
index 00000000..787c4bfc
--- /dev/null
+++ b/pjlib/src/pj/os_timestamp_win32.c
@@ -0,0 +1,38 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/os_timestamp_win32.c 2 10/14/05 12:26a Bennylp $ */
+/*
+ * $Log: /pjproject-0.3/pjlib/src/pj/os_timestamp_win32.c $
+ *
+ * 2 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 1 9/18/05 8:15p Bennylp
+ * Created.
+ *
+ */
+#include <pj/os.h>
+#include <pj/errno.h>
+#include <windows.h>
+
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+ LARGE_INTEGER val;
+
+ if (!QueryPerformanceCounter(&val))
+ return PJ_RETURN_OS_ERROR(GetLastError());
+
+ ts->u64 = val.QuadPart;
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+ LARGE_INTEGER val;
+
+ if (!QueryPerformanceFrequency(&val))
+ return PJ_RETURN_OS_ERROR(GetLastError());
+
+ freq->u64 = val.QuadPart;
+ return PJ_SUCCESS;
+}
+
diff --git a/pjlib/src/pj/pool.c b/pjlib/src/pj/pool.c
new file mode 100644
index 00000000..3bf195b0
--- /dev/null
+++ b/pjlib/src/pj/pool.c
@@ -0,0 +1,265 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/pool.c 8 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/pool.c $
+ *
+ * 8 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 7 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/except.h>
+#include <pj/assert.h>
+#include <pj/os.h>
+#include <pj/compat/sprintf.h>
+
+/* Include inline definitions when inlining is disabled. */
+#if !PJ_FUNCTIONS_ARE_INLINED
+# include <pj/pool_i.h>
+#endif
+
+#define LOG(expr) PJ_LOG(5,expr)
+
+int PJ_NO_MEMORY_EXCEPTION;
+
+/*
+ * Create new block.
+ * Create a new big chunk of memory block, from which user allocation will be
+ * taken from.
+ */
+static pj_pool_block *pj_pool_create_block( pj_pool_t *pool, pj_size_t size)
+{
+ pj_pool_block *block;
+
+ PJ_CHECK_STACK();
+ pj_assert(size >= sizeof(pj_pool_block));
+
+ LOG((pool->obj_name, "create_block(sz=%u), cur.cap=%u, cur.used=%u",
+ size, pool->capacity, pool->used_size));
+
+ /* Request memory from allocator. */
+ block = (pj_pool_block*)
+ (*pool->factory->policy.block_alloc)(pool->factory, size);
+ if (block == NULL) {
+ (*pool->callback)(pool, size);
+ return NULL;
+ }
+
+ /* Add capacity. */
+ pool->capacity += size;
+ pool->used_size += sizeof(pj_pool_block);
+
+ /* Set block attribytes. */
+ block->cur = block->buf = ((unsigned char*)block) + sizeof(pj_pool_block);
+ block->end = ((unsigned char*)block) + size;
+
+ /* Insert in the front of the list. */
+ pj_list_insert_after(&pool->block_list, block);
+
+ LOG((pool->obj_name," block created, buffer=%p-%p",block->buf, block->end));
+
+ return block;
+}
+
+/*
+ * Allocate memory chunk for user from available blocks.
+ * This will iterate through block list to find space to allocate the chunk.
+ * If no space is available in all the blocks, a new block might be created
+ * (depending on whether the pool is allowed to resize).
+ */
+PJ_DEF(void*) pj_pool_allocate_find(pj_pool_t *pool, unsigned size)
+{
+ pj_pool_block *block = pool->block_list.next;
+ void *p;
+ unsigned block_size;
+
+ PJ_CHECK_STACK();
+
+ while (block != &pool->block_list) {
+ p = pj_pool_alloc_from_block(pool, block, size);
+ if (p != NULL)
+ return p;
+ block = block->next;
+ }
+ /* No available space in all blocks. */
+
+ /* If pool is configured NOT to expand, return error. */
+ if (pool->increment_size == 0) {
+ LOG((pool->obj_name, "Can't expand pool to allocate %u bytes "
+ "(used=%u, cap=%u)",
+ size, pool->used_size, pool->capacity));
+ (*pool->callback)(pool, size);
+ return NULL;
+ }
+
+ /* If pool is configured to expand, but the increment size
+ * is less than the required size, expand the pool by multiple
+ * increment size
+ */
+ if (pool->increment_size < size + sizeof(pj_pool_block)) {
+ unsigned count;
+ count = (size + pool->increment_size + sizeof(pj_pool_block)) /
+ pool->increment_size;
+ block_size = count * pool->increment_size;
+
+ } else {
+ block_size = pool->increment_size;
+ }
+
+ LOG((pool->obj_name,
+ "%u bytes requested, resizing pool by %u bytes (used=%u, cap=%u)",
+ size, block_size, pool->used_size, pool->capacity));
+
+ block = pj_pool_create_block(pool, block_size);
+ if (!block)
+ return NULL;
+
+ p = pj_pool_alloc_from_block(pool, block, size);
+ pj_assert(p != NULL);
+#if PJ_DEBUG
+ if (p == NULL) {
+ p = p;
+ }
+#endif
+ return p;
+}
+
+/*
+ * Internal function to initialize pool.
+ */
+PJ_DEF(void) pj_pool_init_int( pj_pool_t *pool,
+ const char *name,
+ pj_size_t increment_size,
+ pj_pool_callback *callback)
+{
+ pj_pool_block *block;
+
+ PJ_CHECK_STACK();
+
+ pool->increment_size = increment_size;
+ pool->callback = callback;
+ pool->used_size = sizeof(*pool);
+ block = pool->block_list.next;
+ while (block != &pool->block_list) {
+ pool->used_size += sizeof(pj_pool_block);
+ block = block->next;
+ }
+
+ if (name) {
+ if (strchr(name, '%') != NULL) {
+ sprintf(pool->obj_name, name, pool);
+ } else {
+ strncpy(pool->obj_name, name, PJ_MAX_OBJ_NAME);
+ }
+ } else {
+ pool->obj_name[0] = '\0';
+ }
+}
+
+/*
+ * Create new memory pool.
+ */
+PJ_DEF(pj_pool_t*) pj_pool_create_int( pj_pool_factory *f, const char *name,
+ pj_size_t initial_size,
+ pj_size_t increment_size,
+ pj_pool_callback *callback)
+{
+ pj_pool_t *pool;
+ pj_pool_block *block;
+ unsigned char *buffer;
+
+ PJ_CHECK_STACK();
+
+ buffer = (*f->policy.block_alloc)(f, initial_size);
+ if (!buffer)
+ return NULL;
+
+ /* Set pool administrative data. */
+ pool = (pj_pool_t*)buffer;
+ pj_memset(pool, 0, sizeof(*pool));
+
+ pj_list_init(&pool->block_list);
+ pool->factory = f;
+
+ /* Create the first block from the memory. */
+ block = (pj_pool_block*) (buffer + sizeof(*pool));
+ block->cur = block->buf = ((unsigned char*)block) + sizeof(pj_pool_block);
+ block->end = buffer + initial_size;
+ pj_list_insert_after(&pool->block_list, block);
+
+ pj_pool_init_int(pool, name, increment_size, callback);
+
+ /* Pool initial capacity and used size */
+ pool->capacity = initial_size;
+
+ LOG((pool->obj_name, "pool created, size=%u", pool->capacity));
+ return pool;
+}
+
+/*
+ * Reset the pool to the state when it was created.
+ * All blocks will be deallocated except the first block. All memory areas
+ * are marked as free.
+ */
+static void reset_pool(pj_pool_t *pool)
+{
+ pj_pool_block *block;
+
+ PJ_CHECK_STACK();
+
+ block = pool->block_list.prev;
+ if (block == &pool->block_list)
+ return;
+
+ /* Skip the first block because it is occupying the same memory
+ as the pool itself.
+ */
+ block = block->prev;
+
+ while (block != &pool->block_list) {
+ pj_pool_block *prev = block->prev;
+ pj_list_erase(block);
+ (*pool->factory->policy.block_free)(pool->factory, block,
+ block->end - (unsigned char*)block);
+ block = prev;
+ }
+
+ block = pool->block_list.next;
+ block->cur = block->buf;
+ pool->capacity = block->end - (unsigned char*)pool;
+ pool->used_size = 0;
+}
+
+/*
+ * The public function to reset pool.
+ */
+PJ_DEF(void) pj_pool_reset(pj_pool_t *pool)
+{
+ LOG((pool->obj_name, "reset(): cap=%d, used=%d(%d%%)",
+ pool->capacity, pool->used_size, pool->used_size*100/pool->capacity));
+
+ reset_pool(pool);
+}
+
+/*
+ * Destroy the pool.
+ */
+PJ_DEF(void) pj_pool_destroy_int(pj_pool_t *pool)
+{
+ pj_size_t initial_size;
+
+ LOG((pool->obj_name, "destroy(): cap=%d, used=%d(%d%%), block0=%p-%p",
+ pool->capacity, pool->used_size, pool->used_size*100/pool->capacity,
+ ((pj_pool_block*)pool->block_list.next)->buf,
+ ((pj_pool_block*)pool->block_list.next)->end));
+
+ reset_pool(pool);
+ initial_size = ((pj_pool_block*)pool->block_list.next)->end -
+ (unsigned char*)pool;
+ (*pool->factory->policy.block_free)(pool->factory, pool, initial_size);
+}
+
+
diff --git a/pjlib/src/pj/pool_caching.c b/pjlib/src/pj/pool_caching.c
new file mode 100644
index 00000000..b72b0d45
--- /dev/null
+++ b/pjlib/src/pj/pool_caching.c
@@ -0,0 +1,210 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/pool_caching.c 5 9/17/05 10:37a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/pool_caching.c $
+ *
+ * 5 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/os.h>
+
+static pj_pool_t* cpool_create_pool(pj_pool_factory *pf,
+ const char *name,
+ pj_size_t initial_size,
+ pj_size_t increment_sz,
+ pj_pool_callback *callback);
+static void cpool_release_pool(pj_pool_factory *pf, pj_pool_t *pool);
+static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail );
+
+static pj_size_t pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE] =
+{
+ 256, 512, 1024, 2048, 4096, 8192, 12288, 16384,
+ 20480, 24576, 28672, 32768, 40960, 49152, 57344, 65536
+};
+
+
+PJ_DEF(void) pj_caching_pool_init( pj_caching_pool *cp,
+ const pj_pool_factory_policy *policy,
+ pj_size_t max_capacity)
+{
+ int i;
+
+ PJ_CHECK_STACK();
+
+ pj_memset(cp, 0, sizeof(*cp));
+
+ cp->max_capacity = max_capacity;
+ pj_list_init(&cp->used_list);
+ for (i=0; i<PJ_CACHING_POOL_ARRAY_SIZE; ++i)
+ pj_list_init(&cp->free_list[i]);
+
+ pj_memcpy(&cp->factory.policy, policy, sizeof(pj_pool_factory_policy));
+ cp->factory.create_pool = &cpool_create_pool;
+ cp->factory.release_pool = &cpool_release_pool;
+ cp->factory.dump_status = &cpool_dump_status;
+}
+
+PJ_DEF(void) pj_caching_pool_destroy( pj_caching_pool *cp )
+{
+ int i;
+ pj_pool_t *pool;
+
+ PJ_CHECK_STACK();
+
+ /* Delete all pool in free list */
+ for (i=0; i < PJ_CACHING_POOL_ARRAY_SIZE; ++i) {
+ pj_pool_t *pool = cp->free_list[i].next;
+ pj_pool_t *next;
+ for (; pool != (void*)&cp->free_list[i]; pool = next) {
+ next = pool->next;
+ pj_list_erase(pool);
+ pj_pool_destroy_int(pool);
+ }
+ }
+
+ /* Delete all pools in used list */
+ pool = cp->used_list.next;
+ while (pool != (pj_pool_t*) &cp->used_list) {
+ pj_pool_t *next = pool->next;
+ pj_list_erase(pool);
+ pj_pool_destroy_int(pool);
+ pool = next;
+ }
+}
+
+static pj_pool_t* cpool_create_pool(pj_pool_factory *pf,
+ const char *name,
+ pj_size_t initial_size,
+ pj_size_t increment_sz,
+ pj_pool_callback *callback)
+{
+ pj_caching_pool *cp = (pj_caching_pool*)pf;
+ pj_pool_t *pool;
+ int idx;
+
+ PJ_CHECK_STACK();
+
+ /* Use pool factory's policy when callback is NULL */
+ if (callback == NULL) {
+ callback = pf->policy.callback;
+ }
+
+ /* Search the suitable size for the pool.
+ * We'll just do linear search to the size array, as the array size itself
+ * is only a few elements. Binary search I suspect will be less efficient
+ * for this purpose.
+ */
+ for (idx=0;
+ idx < PJ_CACHING_POOL_ARRAY_SIZE && pool_sizes[idx] < initial_size;
+ ++idx)
+ ;
+
+ /* Check whether there's a pool in the list. */
+ if (idx==PJ_CACHING_POOL_ARRAY_SIZE || pj_list_empty(&cp->free_list[idx])) {
+ /* No pool is available. */
+ /* Set minimum size. */
+ if (idx < PJ_CACHING_POOL_ARRAY_SIZE)
+ initial_size = pool_sizes[idx];
+
+ /* Create new pool */
+ pool = pj_pool_create_int(&cp->factory, name, initial_size,
+ increment_sz, callback);
+ if (!pool)
+ return NULL;
+
+ } else {
+ /* Get one pool from the list. */
+ pool = cp->free_list[idx].next;
+ pj_list_erase(pool);
+
+ /* Initialize the pool. */
+ pj_pool_init_int(pool, name, increment_sz, callback);
+
+ /* Update pool manager's free capacity. */
+ cp->capacity -= pj_pool_get_capacity(pool);
+
+ PJ_LOG(5, (pool->obj_name, "pool reused, size=%u", pool->capacity));
+ }
+
+ /* Put in used list. */
+ pj_list_insert_before( &cp->used_list, pool );
+
+ /* Increment used count. */
+ ++cp->used_count;
+ return pool;
+}
+
+static void cpool_release_pool( pj_pool_factory *pf, pj_pool_t *pool)
+{
+ pj_caching_pool *cp = (pj_caching_pool*)pf;
+ int i;
+
+ PJ_CHECK_STACK();
+
+ /* Erase from the used list. */
+ pj_list_erase(pool);
+
+ /* Decrement used count. */
+ --cp->used_count;
+
+ /* Destroy the pool if the size is greater than our size or if the total
+ * capacity in our recycle list (plus the size of the pool) exceeds
+ * maximum capacity.
+ . */
+ if (pool->capacity > pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE-1] ||
+ cp->capacity + pool->capacity > cp->max_capacity)
+ {
+ pj_pool_destroy_int(pool);
+ return;
+ }
+
+ /* Reset pool. */
+ PJ_LOG(4, (pool->obj_name, "recycle(): cap=%d, used=%d(%d%%)",
+ pool->capacity, pool->used_size, pool->used_size*100/pool->capacity));
+ pj_pool_reset(pool);
+
+ /*
+ * Otherwise put the pool in our recycle list.
+ */
+ for (i=0; i < PJ_CACHING_POOL_ARRAY_SIZE && pool_sizes[i] != pool->capacity; ++i)
+ ;
+
+ pj_assert( i != PJ_CACHING_POOL_ARRAY_SIZE );
+ if (i == PJ_CACHING_POOL_ARRAY_SIZE) {
+ /* Something has gone wrong with the pool. */
+ pj_pool_destroy_int(pool);
+ return;
+ }
+
+ pj_list_insert_after(&cp->free_list[i], pool);
+ cp->capacity += pool->capacity;
+}
+
+static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail )
+{
+#if PJ_LOG_MAX_LEVEL >= 3
+ pj_caching_pool *cp = (pj_caching_pool*)factory;
+ PJ_LOG(3,("cachpool", " Dumping caching pool:"));
+ PJ_LOG(3,("cachpool", " Capacity=%u, max_capacity=%u, used_cnt=%u", \
+ cp->capacity, cp->max_capacity, cp->used_count));
+ if (detail) {
+ pj_pool_t *pool = cp->used_list.next;
+ pj_uint32_t total_used = 0, total_capacity = 0;
+ PJ_LOG(3,("cachpool", " Dumping all active pools:"));
+ while (pool != (void*)&cp->used_list) {
+ PJ_LOG(3,("cachpool", " %12s: %8d of %8d (%d%%) used", pool->obj_name,
+ pool->used_size, pool->capacity,
+ pool->used_size*100/pool->capacity));
+ total_used += pool->used_size;
+ total_capacity += pool->capacity;
+ pool = pool->next;
+ }
+ PJ_LOG(3,("cachpool", " Total %9d of %9d (%d %%) used!",
+ total_used, total_capacity,
+ total_used * 100 / total_capacity));
+ }
+#endif
+}
diff --git a/pjlib/src/pj/pool_dbg_win32.c b/pjlib/src/pj/pool_dbg_win32.c
new file mode 100644
index 00000000..4419d048
--- /dev/null
+++ b/pjlib/src/pj/pool_dbg_win32.c
@@ -0,0 +1,226 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/pool_dbg_win32.c 4 9/17/05 10:37a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/pool_dbg_win32.c $
+ *
+ * 4 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/pool.h>
+
+/* Only if we ARE debugging memory allocations. */
+#if PJ_POOL_DEBUG
+
+#include <pj/list.h>
+#include <pj/log.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+typedef struct memory_entry
+{
+ PJ_DECL_LIST_MEMBER(struct memory_entry)
+ void *ptr;
+ char *file;
+ int line;
+} memory_entry;
+
+struct pj_pool_t
+{
+ char obj_name[32];
+ HANDLE hHeap;
+ memory_entry first;
+ pj_size_t initial_size;
+ pj_size_t increment;
+ pj_size_t used_size;
+ char *file;
+ int line;
+};
+
+PJ_DEF(void) pj_pool_set_functions( void *(*malloc_func)(pj_size_t),
+ void (*free_func)(void *ptr, pj_size_t))
+{
+ /* Ignored. */
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(malloc_func)
+ PJ_UNUSED_ARG(free_func)
+}
+
+PJ_DEF(pj_pool_t*) pj_pool_create_dbg( const char *name,
+ pj_size_t initial_size,
+ pj_size_t increment_size,
+ pj_pool_callback *callback,
+ char *file, int line)
+{
+ pj_pool_t *pool;
+ HANDLE hHeap;
+
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(callback)
+
+ /* Create Win32 heap for the pool. */
+ hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE,
+ initial_size, 0);
+ if (!hHeap) {
+ return NULL;
+ }
+
+
+ /* Create and initialize the pool structure. */
+ pool = HeapAlloc(hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE,
+ sizeof(*pool));
+ memset(pool, 0, sizeof(*pool));
+ pool->file = file;
+ pool->line = line;
+ pool->hHeap = hHeap;
+ pool->initial_size = initial_size;
+ pool->increment = increment_size;
+ pool->used_size = 0;
+
+ /* Set name. */
+ if (name) {
+ if (strchr(name, '%') != NULL) {
+ sprintf(pool->obj_name, name, pool);
+ } else {
+ strncpy(pool->obj_name, name, PJ_MAX_OBJ_NAME);
+ }
+ } else {
+ pool->obj_name[0] = '\0';
+ }
+
+ /* List pool's entry. */
+ pj_list_init(&pool->first);
+
+ PJ_LOG(3,(pool->obj_name, "Pool created"));
+ return pool;
+}
+
+PJ_DEF(void) pj_pool_destroy( pj_pool_t *pool )
+{
+ memory_entry *entry;
+
+ PJ_CHECK_STACK();
+
+ PJ_LOG(3,(pool->obj_name, "Destoying pool, init_size=%u, used=%u",
+ pool->initial_size, pool->used_size));
+
+ if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, pool)) {
+ PJ_LOG(2,(pool->obj_name, "Corrupted pool structure, allocated in %s:%d",
+ pool->file, pool->line));
+ }
+
+ /* Validate all memory entries in the pool. */
+ for (entry=pool->first.next; entry != &pool->first; entry = entry->next) {
+ if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, entry)) {
+ PJ_LOG(2,(pool->obj_name, "Corrupted pool entry, allocated in %s:%d",
+ entry->file, entry->line));
+ }
+
+ if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, entry->ptr)) {
+ PJ_LOG(2,(pool->obj_name, "Corrupted pool memory, allocated in %s:%d",
+ entry->file, entry->line));
+ }
+ }
+
+ /* Destroy heap. */
+ HeapDestroy(pool->hHeap);
+}
+
+PJ_DEF(void) pj_pool_reset( pj_pool_t *pool )
+{
+ /* Do nothing. */
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(pool)
+}
+
+PJ_DEF(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool )
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(pool)
+ return 0;
+}
+
+PJ_DEF(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool )
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(pool)
+ return 0;
+}
+
+PJ_DEF(pj_size_t) pj_pool_get_request_count( pj_pool_t *pool )
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(pool)
+ return 0;
+}
+
+PJ_DEF(void*) pj_pool_alloc_dbg( pj_pool_t *pool, pj_size_t size,
+ char *file, int line)
+{
+ memory_entry *entry;
+ int entry_size;
+
+ PJ_CHECK_STACK();
+
+ entry_size = sizeof(*entry);
+ entry = HeapAlloc(pool->hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE,
+ entry_size);
+ entry->file = file;
+ entry->line = line;
+ entry->ptr = HeapAlloc(pool->hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE,
+ size);
+ pj_list_insert_before( &pool->first, entry);
+
+ pool->used_size += size;
+ return entry->ptr;
+}
+
+PJ_DEF(void*) pj_pool_calloc_dbg( pj_pool_t *pool, pj_size_t count, pj_size_t elem,
+ char *file, int line)
+{
+ void *ptr;
+
+ PJ_CHECK_STACK();
+
+ ptr = pj_pool_alloc_dbg(pool, count*elem, file, line);
+ memset(ptr, 0, count*elem);
+ return ptr;
+}
+
+
+PJ_DEF(void) pj_pool_pool_init( pj_pool_pool_t *pool_pool,
+ pj_size_t max_capacity)
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(pool_pool)
+ PJ_UNUSED_ARG(max_capacity)
+}
+
+PJ_DEF(void) pj_pool_pool_destroy( pj_pool_pool_t *pool_pool )
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(pool_pool)
+}
+
+PJ_DEF(pj_pool_t*) pj_pool_pool_create_pool( pj_pool_pool_t *pool_pool,
+ const char *name,
+ pj_size_t initial_size,
+ pj_size_t increment_size,
+ pj_pool_callback *callback)
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(pool_pool)
+ return pj_pool_create(name, initial_size, increment_size, callback);
+}
+
+PJ_DEF(void) pj_pool_pool_release_pool( pj_pool_pool_t *pool_pool,
+ pj_pool_t *pool )
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(pool_pool)
+ pj_pool_destroy(pool);
+}
+
+
+#endif /* PJ_POOL_DEBUG */
diff --git a/pjlib/src/pj/pool_policy_kmalloc.c b/pjlib/src/pj/pool_policy_kmalloc.c
new file mode 100644
index 00000000..4accff7d
--- /dev/null
+++ b/pjlib/src/pj/pool_policy_kmalloc.c
@@ -0,0 +1,54 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/pool_policy_kmalloc.c 3 10/29/05 11:51a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/pool_policy_kmalloc.c $
+ *
+ * 3 10/29/05 11:51a Bennylp
+ * Version 0.3-pre2.
+ *
+ * 2 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 1 9/22/05 10:40a Bennylp
+ * Created.
+ *
+ */
+#include <pj/pool.h>
+#include <pj/except.h>
+#include <pj/os.h>
+
+
+static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size)
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(factory);
+
+ return kmalloc(size, GFP_ATOMIC);
+}
+
+static void default_block_free(pj_pool_factory *factory,
+ void *mem, pj_size_t size)
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(factory);
+ PJ_UNUSED_ARG(size);
+
+ kfree(mem);
+}
+
+static void default_pool_callback(pj_pool_t *pool, pj_size_t size)
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(size);
+
+ PJ_THROW(PJ_NO_MEMORY_EXCEPTION);
+}
+
+pj_pool_factory_policy pj_pool_factory_default_policy =
+{
+ &default_block_alloc,
+ &default_block_free,
+ &default_pool_callback,
+ 0
+};
+
diff --git a/pjlib/src/pj/pool_policy_malloc.c b/pjlib/src/pj/pool_policy_malloc.c
new file mode 100644
index 00000000..12eb7c34
--- /dev/null
+++ b/pjlib/src/pj/pool_policy_malloc.c
@@ -0,0 +1,58 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/pool_policy_malloc.c 2 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/pool_policy_malloc.c $
+ *
+ * 2 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 1 9/21/05 1:37p Bennylp
+ * Renamed from pool_policy.c
+ *
+ * 3 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/pool.h>
+#include <pj/except.h>
+#include <pj/os.h>
+#include <pj/compat/malloc.h>
+
+/*
+ * This file contains pool default policy definition and implementation.
+ */
+
+
+static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size)
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(factory);
+ PJ_UNUSED_ARG(size);
+
+ return malloc(size);
+}
+
+static void default_block_free(pj_pool_factory *factory, void *mem, pj_size_t size)
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(factory);
+ PJ_UNUSED_ARG(size);
+
+ free(mem);
+}
+
+static void default_pool_callback(pj_pool_t *pool, pj_size_t size)
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(size);
+
+ PJ_THROW(PJ_NO_MEMORY_EXCEPTION);
+}
+
+pj_pool_factory_policy pj_pool_factory_default_policy =
+{
+ &default_block_alloc,
+ &default_block_free,
+ &default_pool_callback,
+ 0
+};
diff --git a/pjlib/src/pj/rand.c b/pjlib/src/pj/rand.c
new file mode 100644
index 00000000..6d25670f
--- /dev/null
+++ b/pjlib/src/pj/rand.c
@@ -0,0 +1,29 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/rand.c 3 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/rand.c $
+ *
+ * 3 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 2 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ * 1 9/15/05 8:40p Bennylp
+ * Created.
+ */
+#include <pj/rand.h>
+#include <pj/os.h>
+#include <pj/compat/rand.h>
+
+PJ_DEF(void) pj_srand(unsigned int seed)
+{
+ PJ_CHECK_STACK();
+ platform_srand(seed);
+}
+
+PJ_DEF(int) pj_rand(void)
+{
+ PJ_CHECK_STACK();
+ return platform_rand();
+}
+
diff --git a/pjlib/src/pj/rbtree.c b/pjlib/src/pj/rbtree.c
new file mode 100644
index 00000000..582a6f75
--- /dev/null
+++ b/pjlib/src/pj/rbtree.c
@@ -0,0 +1,416 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/rbtree.c 5 9/17/05 10:37a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/rbtree.c $
+ *
+ * 5 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/rbtree.h>
+#include <pj/os.h>
+
+static void left_rotate( pj_rbtree *tree, pj_rbtree_node *node )
+{
+ pj_rbtree_node *rnode, *parent;
+
+ PJ_CHECK_STACK();
+
+ rnode = node->right;
+ if (rnode == tree->null)
+ return;
+
+ node->right = rnode->left;
+ if (rnode->left != tree->null)
+ rnode->left->parent = node;
+ parent = node->parent;
+ rnode->parent = parent;
+ if (parent != tree->null) {
+ if (parent->left == node)
+ parent->left = rnode;
+ else
+ parent->right = rnode;
+ } else {
+ tree->root = rnode;
+ }
+ rnode->left = node;
+ node->parent = rnode;
+}
+
+static void right_rotate( pj_rbtree *tree, pj_rbtree_node *node )
+{
+ pj_rbtree_node *lnode, *parent;
+
+ PJ_CHECK_STACK();
+
+ lnode = node->left;
+ if (lnode == tree->null)
+ return;
+
+ node->left = lnode->right;
+ if (lnode->right != tree->null)
+ lnode->right->parent = node;
+ parent = node->parent;
+ lnode->parent = parent;
+
+ if (parent != tree->null) {
+ if (parent->left == node)
+ parent->left = lnode;
+ else
+ parent->right = lnode;
+ } else {
+ tree->root = lnode;
+ }
+ lnode->right = node;
+ node->parent = lnode;
+}
+
+static void insert_fixup( pj_rbtree *tree, pj_rbtree_node *node )
+{
+ pj_rbtree_node *temp, *parent;
+
+ PJ_CHECK_STACK();
+
+ while (node != tree->root && node->parent->color == PJ_RBCOLOR_RED) {
+ parent = node->parent;
+ if (parent == parent->parent->left) {
+ temp = parent->parent->right;
+ if (temp->color == PJ_RBCOLOR_RED) {
+ temp->color = PJ_RBCOLOR_BLACK;
+ node = parent;
+ node->color = PJ_RBCOLOR_BLACK;
+ node = node->parent;
+ node->color = PJ_RBCOLOR_RED;
+ } else {
+ if (node == parent->right) {
+ node = parent;
+ left_rotate(tree, node);
+ }
+ temp = node->parent;
+ temp->color = PJ_RBCOLOR_BLACK;
+ temp = temp->parent;
+ temp->color = PJ_RBCOLOR_RED;
+ right_rotate( tree, temp);
+ }
+ } else {
+ temp = parent->parent->left;
+ if (temp->color == PJ_RBCOLOR_RED) {
+ temp->color = PJ_RBCOLOR_BLACK;
+ node = parent;
+ node->color = PJ_RBCOLOR_BLACK;
+ node = node->parent;
+ node->color = PJ_RBCOLOR_RED;
+ } else {
+ if (node == parent->left) {
+ node = parent;
+ right_rotate(tree, node);
+ }
+ temp = node->parent;
+ temp->color = PJ_RBCOLOR_BLACK;
+ temp = temp->parent;
+ temp->color = PJ_RBCOLOR_RED;
+ left_rotate(tree, temp);
+ }
+ }
+ }
+
+ tree->root->color = PJ_RBCOLOR_BLACK;
+}
+
+
+static void delete_fixup( pj_rbtree *tree, pj_rbtree_node *node )
+{
+ pj_rbtree_node *temp;
+
+ PJ_CHECK_STACK();
+
+ while (node != tree->root && node->color == PJ_RBCOLOR_BLACK) {
+ if (node->parent->left == node) {
+ temp = node->parent->right;
+ if (temp->color == PJ_RBCOLOR_RED) {
+ temp->color = PJ_RBCOLOR_BLACK;
+ node->parent->color = PJ_RBCOLOR_RED;
+ left_rotate(tree, node->parent);
+ temp = node->parent->right;
+ }
+ if (temp->left->color == PJ_RBCOLOR_BLACK &&
+ temp->right->color == PJ_RBCOLOR_BLACK)
+ {
+ temp->color = PJ_RBCOLOR_RED;
+ node = node->parent;
+ } else {
+ if (temp->right->color == PJ_RBCOLOR_BLACK) {
+ temp->left->color = PJ_RBCOLOR_BLACK;
+ temp->color = PJ_RBCOLOR_RED;
+ right_rotate( tree, temp);
+ temp = node->parent->right;
+ }
+ temp->color = node->parent->color;
+ temp->right->color = PJ_RBCOLOR_BLACK;
+ node->parent->color = PJ_RBCOLOR_BLACK;
+ left_rotate(tree, node->parent);
+ node = tree->root;
+ }
+ } else {
+ temp = node->parent->left;
+ if (temp->color == PJ_RBCOLOR_RED) {
+ temp->color = PJ_RBCOLOR_BLACK;
+ node->parent->color = PJ_RBCOLOR_RED;
+ right_rotate( tree, node->parent);
+ temp = node->parent->left;
+ }
+ if (temp->right->color == PJ_RBCOLOR_BLACK &&
+ temp->left->color == PJ_RBCOLOR_BLACK)
+ {
+ temp->color = PJ_RBCOLOR_RED;
+ node = node->parent;
+ } else {
+ if (temp->left->color == PJ_RBCOLOR_BLACK) {
+ temp->right->color = PJ_RBCOLOR_BLACK;
+ temp->color = PJ_RBCOLOR_RED;
+ left_rotate( tree, temp);
+ temp = node->parent->left;
+ }
+ temp->color = node->parent->color;
+ node->parent->color = PJ_RBCOLOR_BLACK;
+ temp->left->color = PJ_RBCOLOR_BLACK;
+ right_rotate(tree, node->parent);
+ node = tree->root;
+ }
+ }
+ }
+
+ node->color = PJ_RBCOLOR_BLACK;
+}
+
+
+PJ_DEF(void) pj_rbtree_init( pj_rbtree *tree, pj_rbtree_comp *comp )
+{
+ PJ_CHECK_STACK();
+
+ tree->null = tree->root = &tree->null_node;
+ tree->null->key = NULL;
+ tree->null->user_data = NULL;
+ tree->size = 0;
+ tree->null->left = tree->null->right = tree->null->parent = tree->null;
+ tree->null->color = PJ_RBCOLOR_BLACK;
+ tree->comp = comp;
+}
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_first( pj_rbtree *tree )
+{
+ register pj_rbtree_node *node = tree->root;
+ register pj_rbtree_node *null = tree->null;
+
+ PJ_CHECK_STACK();
+
+ while (node->left != null)
+ node = node->left;
+ return node != null ? node : NULL;
+}
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_last( pj_rbtree *tree )
+{
+ register pj_rbtree_node *node = tree->root;
+ register pj_rbtree_node *null = tree->null;
+
+ PJ_CHECK_STACK();
+
+ while (node->right != null)
+ node = node->right;
+ return node != null ? node : NULL;
+}
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_next( pj_rbtree *tree,
+ register pj_rbtree_node *node )
+{
+ register pj_rbtree_node *null = tree->null;
+
+ PJ_CHECK_STACK();
+
+ if (node->right != null) {
+ for (node=node->right; node->left!=null; node = node->left)
+ /* void */;
+ } else {
+ register pj_rbtree_node *temp = node->parent;
+ while (temp!=null && temp->right==node) {
+ node = temp;
+ temp = temp->parent;
+ }
+ node = temp;
+ }
+ return node != null ? node : NULL;
+}
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_prev( pj_rbtree *tree,
+ register pj_rbtree_node *node )
+{
+ register pj_rbtree_node *null = tree->null;
+
+ PJ_CHECK_STACK();
+
+ if (node->left != null) {
+ for (node=node->left; node->right!=null; node=node->right)
+ /* void */;
+ } else {
+ register pj_rbtree_node *temp = node->parent;
+ while (temp!=null && temp->left==node) {
+ node = temp;
+ temp = temp->parent;
+ }
+ node = temp;
+ }
+ return node != null ? node : NULL;
+}
+
+PJ_DEF(int) pj_rbtree_insert( pj_rbtree *tree,
+ pj_rbtree_node *element )
+{
+ int rv = 0;
+ pj_rbtree_node *node, *parent = tree->null,
+ *null = tree->null;
+ pj_rbtree_comp *comp = tree->comp;
+
+ PJ_CHECK_STACK();
+
+ node = tree->root;
+ while (node != null) {
+ rv = (*comp)(element->key, node->key);
+ if (rv == 0) {
+ /* found match, i.e. entry with equal key already exist */
+ return -1;
+ }
+ parent = node;
+ node = rv < 0 ? node->left : node->right;
+ }
+
+ element->color = PJ_RBCOLOR_RED;
+ element->left = element->right = null;
+
+ node = element;
+ if (parent != null) {
+ node->parent = parent;
+ if (rv < 0)
+ parent->left = node;
+ else
+ parent->right = node;
+ insert_fixup( tree, node);
+ } else {
+ tree->root = node;
+ node->parent = null;
+ node->color = PJ_RBCOLOR_BLACK;
+ }
+
+ ++tree->size;
+ return 0;
+}
+
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_find( pj_rbtree *tree,
+ const void *key )
+{
+ int rv;
+ pj_rbtree_node *node = tree->root;
+ pj_rbtree_node *null = tree->null;
+ pj_rbtree_comp *comp = tree->comp;
+
+ while (node != null) {
+ rv = (*comp)(key, node->key);
+ if (rv == 0)
+ return node;
+ node = rv < 0 ? node->left : node->right;
+ }
+ return node != null ? node : NULL;
+}
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_erase( pj_rbtree *tree,
+ pj_rbtree_node *node )
+{
+ pj_rbtree_node *succ;
+ pj_rbtree_node *null = tree->null;
+ pj_rbtree_node *child;
+ pj_rbtree_node *parent;
+
+ PJ_CHECK_STACK();
+
+ if (node->left == null || node->right == null) {
+ succ = node;
+ } else {
+ for (succ=node->right; succ->left!=null; succ=succ->left)
+ /* void */;
+ }
+
+ child = succ->left != null ? succ->left : succ->right;
+ parent = succ->parent;
+ child->parent = parent;
+
+ if (parent != null) {
+ if (parent->left == succ)
+ parent->left = child;
+ else
+ parent->right = child;
+ } else
+ tree->root = child;
+
+ if (succ != node) {
+ succ->parent = node->parent;
+ succ->left = node->left;
+ succ->right = node->right;
+ succ->color = node->color;
+
+ parent = node->parent;
+ if (parent != null) {
+ if (parent->left==node)
+ parent->left=succ;
+ else
+ parent->right=succ;
+ }
+ if (node->left != null)
+ node->left->parent = succ;;
+ if (node->right != null)
+ node->right->parent = succ;
+
+ if (tree->root == node)
+ tree->root = succ;
+ }
+
+ if (succ->color == PJ_RBCOLOR_BLACK) {
+ if (child != null)
+ delete_fixup(tree, child);
+ tree->null->color = PJ_RBCOLOR_BLACK;
+ }
+
+ --tree->size;
+ return node;
+}
+
+
+PJ_DEF(unsigned) pj_rbtree_max_height( pj_rbtree *tree,
+ pj_rbtree_node *node )
+{
+ unsigned l, r;
+
+ PJ_CHECK_STACK();
+
+ if (node==NULL)
+ node = tree->root;
+
+ l = node->left != tree->null ? pj_rbtree_max_height(tree,node->left)+1 : 0;
+ r = node->right != tree->null ? pj_rbtree_max_height(tree,node->right)+1 : 0;
+ return l > r ? l : r;
+}
+
+PJ_DEF(unsigned) pj_rbtree_min_height( pj_rbtree *tree,
+ pj_rbtree_node *node )
+{
+ unsigned l, r;
+
+ PJ_CHECK_STACK();
+
+ if (node==NULL)
+ node=tree->root;
+
+ l = (node->left != tree->null) ? pj_rbtree_max_height(tree,node->left)+1 : 0;
+ r = (node->right != tree->null) ? pj_rbtree_max_height(tree,node->right)+1 : 0;
+ return l > r ? r : l;
+}
+
+
diff --git a/pjlib/src/pj/scanner.c b/pjlib/src/pj/scanner.c
new file mode 100644
index 00000000..08c7c757
--- /dev/null
+++ b/pjlib/src/pj/scanner.c
@@ -0,0 +1,556 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/scanner.c 9 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/scanner.c $
+ *
+ * 9 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 8 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 7 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/scanner.h>
+#include <pj/string.h>
+#include <pj/except.h>
+#include <pj/os.h>
+
+#define PJ_SCAN_IS_SPACE(c) ((c)==' ' || (c)=='\t')
+#define PJ_SCAN_IS_NEWLINE(c) ((c)=='\r' || (c)=='\n')
+#define PJ_SCAN_CHECK_EOF(s) (s != end)
+
+
+static void pj_scan_syntax_err(pj_scanner *scanner)
+{
+ (*scanner->callback)(scanner);
+}
+
+PJ_DEF(void) pj_cs_init( pj_char_spec cs)
+{
+ PJ_CHECK_STACK();
+ memset(cs, 0, sizeof(cs));
+}
+
+PJ_DEF(void) pj_cs_set( pj_char_spec cs, int c)
+{
+ PJ_CHECK_STACK();
+ cs[c] = 1;
+}
+
+PJ_DEF(void) pj_cs_add_range( pj_char_spec cs, int cstart, int cend)
+{
+ PJ_CHECK_STACK();
+ while (cstart != cend)
+ cs[cstart++] = 1;
+}
+
+PJ_DEF(void) pj_cs_add_alpha( pj_char_spec cs)
+{
+ pj_cs_add_range( cs, 'a', 'z'+1);
+ pj_cs_add_range( cs, 'A', 'Z'+1);
+}
+
+PJ_DEF(void) pj_cs_add_num( pj_char_spec cs)
+{
+ pj_cs_add_range( cs, '0', '9'+1);
+}
+
+PJ_DEF(void) pj_cs_add_str( pj_char_spec cs, const char *str)
+{
+ PJ_CHECK_STACK();
+ while (*str) {
+ cs[(int)*str] = 1;
+ ++str;
+ }
+}
+
+PJ_DEF(void) pj_cs_del_range( pj_char_spec cs, int cstart, int cend)
+{
+ PJ_CHECK_STACK();
+ while (cstart != cend)
+ cs[cstart++] = 0;
+}
+
+PJ_DEF(void) pj_cs_del_str( pj_char_spec cs, const char *str)
+{
+ PJ_CHECK_STACK();
+ while (*str) {
+ cs[(int)*str] = 0;
+ ++str;
+ }
+}
+
+PJ_DEF(void) pj_cs_invert( pj_char_spec cs )
+{
+ unsigned i;
+ PJ_CHECK_STACK();
+ for (i=0; i<sizeof(pj_char_spec)/sizeof(cs[0]); ++i) {
+ cs[i] = (pj_char_spec_element_t) !cs[i];
+ }
+}
+
+PJ_DEF(void) pj_scan_init( pj_scanner *scanner, char *bufstart, int buflen,
+ unsigned options, pj_syn_err_func_ptr callback )
+{
+ PJ_CHECK_STACK();
+
+ scanner->begin = scanner->curptr = bufstart;
+ scanner->end = bufstart + buflen;
+ scanner->line = 1;
+ scanner->col = 1;
+ scanner->callback = callback;
+ scanner->skip_ws = options;
+
+ if (scanner->skip_ws)
+ pj_scan_skip_whitespace(scanner);
+
+ scanner->col = scanner->curptr - scanner->begin + 1;
+}
+
+
+PJ_DEF(void) pj_scan_fini( pj_scanner *scanner )
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(scanner);
+}
+
+PJ_DEF(void) pj_scan_skip_whitespace( pj_scanner *scanner )
+{
+ register char *s = scanner->curptr;
+
+ PJ_CHECK_STACK();
+
+ while (PJ_SCAN_IS_SPACE(*s)) {
+ ++s;
+ }
+
+ if ((scanner->skip_ws & PJ_SCAN_AUTOSKIP_NEWLINE) && PJ_SCAN_IS_NEWLINE(*s)) {
+ for (;;) {
+ if (*s == '\r') {
+ ++s;
+ if (*s == '\n') ++s;
+ ++scanner->line;
+ scanner->col = 1;
+ scanner->curptr = s;
+ } else if (*s == '\n') {
+ ++s;
+ ++scanner->line;
+ scanner->col = 1;
+ scanner->curptr = s;
+ } else if (PJ_SCAN_IS_SPACE(*s)) {
+ do {
+ ++s;
+ } while (PJ_SCAN_IS_SPACE(*s));
+ } else {
+ break;
+ }
+ }
+ }
+
+ if (PJ_SCAN_IS_NEWLINE(*s) && (scanner->skip_ws & PJ_SCAN_AUTOSKIP_WS_HEADER)==PJ_SCAN_AUTOSKIP_WS_HEADER) {
+ /* Check for header continuation. */
+ scanner->col += s - scanner->curptr;
+ scanner->curptr = s;
+
+ if (*s == '\r') {
+ ++s;
+ }
+ if (*s == '\n') {
+ ++s;
+ }
+ if (PJ_SCAN_IS_SPACE(*s)) {
+ register char *t = s;
+ do {
+ ++t;
+ } while (PJ_SCAN_IS_SPACE(*t));
+
+ ++scanner->line;
+ scanner->col = t-s;
+ scanner->curptr = t;
+ }
+ } else {
+ scanner->col += s - scanner->curptr;
+ scanner->curptr = s;
+ }
+}
+
+PJ_DEF(int) pj_scan_peek( pj_scanner *scanner,
+ const pj_char_spec spec, pj_str_t *out)
+{
+ register char *s = scanner->curptr;
+ register char *end = scanner->end;
+
+ PJ_CHECK_STACK();
+
+ if (pj_scan_is_eof(scanner)) {
+ pj_scan_syntax_err(scanner);
+ return -1;
+ }
+
+ while (PJ_SCAN_CHECK_EOF(s) && pj_cs_match(spec, *s))
+ ++s;
+
+ pj_strset3(out, scanner->curptr, s);
+ return s < scanner->end ? *s : 0;
+}
+
+
+PJ_DEF(int) pj_scan_peek_n( pj_scanner *scanner,
+ pj_size_t len, pj_str_t *out)
+{
+ char *endpos = scanner->curptr + len;
+
+ PJ_CHECK_STACK();
+
+ if (endpos > scanner->end) {
+ pj_scan_syntax_err(scanner);
+ return -1;
+ }
+
+ pj_strset(out, scanner->curptr, len);
+ return *endpos;
+}
+
+
+PJ_DEF(int) pj_scan_peek_until( pj_scanner *scanner,
+ const pj_char_spec spec,
+ pj_str_t *out)
+{
+ register char *s = scanner->curptr;
+ register char *end = scanner->end;
+
+ PJ_CHECK_STACK();
+
+ if (pj_scan_is_eof(scanner)) {
+ pj_scan_syntax_err(scanner);
+ return -1;
+ }
+
+ while (PJ_SCAN_CHECK_EOF(s) && !pj_cs_match( spec, *s))
+ ++s;
+
+ pj_strset3(out, scanner->curptr, s);
+ return s!=scanner->end ? *s : 0;
+}
+
+
+PJ_DEF(void) pj_scan_get( pj_scanner *scanner,
+ const pj_char_spec spec, pj_str_t *out)
+{
+ register char *s = scanner->curptr;
+ register char *end = scanner->end;
+ char *start = s;
+
+ PJ_CHECK_STACK();
+
+ if (pj_scan_is_eof(scanner) || !pj_cs_match(spec, *s)) {
+ pj_scan_syntax_err(scanner);
+ return;
+ }
+
+ do {
+ ++s;
+ } while (PJ_SCAN_CHECK_EOF(s) && pj_cs_match(spec, *s));
+
+ pj_strset3(out, scanner->curptr, s);
+
+ scanner->col += (s - start);
+ scanner->curptr = s;
+
+ if (scanner->skip_ws) {
+ pj_scan_skip_whitespace(scanner);
+ }
+}
+
+
+PJ_DEF(void) pj_scan_get_quote( pj_scanner *scanner,
+ int begin_quote, int end_quote,
+ pj_str_t *out)
+{
+ register char *s = scanner->curptr;
+ register char *end = scanner->end;
+ char *start = s;
+
+ PJ_CHECK_STACK();
+
+ /* Check and eat the begin_quote. */
+ if (*s != begin_quote) {
+ pj_scan_syntax_err(scanner);
+ return;
+ }
+ ++s;
+
+ /* Loop until end_quote is found.
+ */
+ do {
+ /* loop until end_quote is found. */
+ do {
+ ++s;
+ } while (s != end && *s != '\n' && *s != end_quote);
+
+ /* check that no backslash character precedes the end_quote. */
+ if (*s == end_quote) {
+ if (*(s-1) == '\\') {
+ if (s-2 == scanner->begin) {
+ break;
+ } else {
+ char *q = s-2;
+ char *r = s-2;
+
+ while (r != scanner->begin && *r == '\\') {
+ --r;
+ }
+ /* break from main loop if we have odd number of backslashes */
+ if (((unsigned)(q-r) & 0x01) == 1) {
+ break;
+ }
+ }
+ } else {
+ /* end_quote is not preceeded by backslash. break now. */
+ break;
+ }
+ } else {
+ /* loop ended by non-end_quote character. break now. */
+ break;
+ }
+ } while (1);
+
+ /* Check and eat the end quote. */
+ if (*s != end_quote) {
+ pj_scan_syntax_err(scanner);
+ return;
+ }
+ ++s;
+
+ pj_strset3(out, scanner->curptr, s);
+
+ scanner->col += (s - start);
+ scanner->curptr = s;
+
+ if (scanner->skip_ws) {
+ pj_scan_skip_whitespace(scanner);
+ }
+}
+
+PJ_DEF(void) pj_scan_get_n( pj_scanner *scanner,
+ unsigned N, pj_str_t *out)
+{
+ register char *s = scanner->curptr;
+ char *start = scanner->curptr;
+
+ PJ_CHECK_STACK();
+
+ if (scanner->curptr + N > scanner->end) {
+ pj_scan_syntax_err(scanner);
+ return;
+ }
+
+ pj_strset(out, s, N);
+
+ s += N;
+ scanner->col += (s - start);
+ scanner->curptr = s;
+
+ if (scanner->skip_ws) {
+ pj_scan_skip_whitespace(scanner);
+ }
+}
+
+
+PJ_DEF(int) pj_scan_get_char( pj_scanner *scanner )
+{
+ char *start = scanner->curptr;
+ int chr = *start;
+
+ PJ_CHECK_STACK();
+
+ if (pj_scan_is_eof(scanner)) {
+ pj_scan_syntax_err(scanner);
+ return 0;
+ }
+
+ ++scanner->curptr;
+ scanner->col += (scanner->curptr - start);
+
+ if (scanner->skip_ws) {
+ pj_scan_skip_whitespace(scanner);
+ }
+ return chr;
+}
+
+
+PJ_DEF(void) pj_scan_get_newline( pj_scanner *scanner )
+{
+ PJ_CHECK_STACK();
+
+ if (!PJ_SCAN_IS_NEWLINE(*scanner->curptr)) {
+ pj_scan_syntax_err(scanner);
+ return;
+ }
+
+ if (*scanner->curptr == '\r') {
+ ++scanner->curptr;
+ }
+ if (*scanner->curptr == '\n') {
+ ++scanner->curptr;
+ }
+
+ ++scanner->line;
+ scanner->col = 1;
+
+ if (scanner->skip_ws) {
+ pj_scan_skip_whitespace(scanner);
+ }
+}
+
+
+PJ_DEF(void) pj_scan_get_until( pj_scanner *scanner,
+ const pj_char_spec spec, pj_str_t *out)
+{
+ register char *s = scanner->curptr;
+ register char *end = scanner->end;
+ char *start = s;
+
+ PJ_CHECK_STACK();
+
+ if (pj_scan_is_eof(scanner)) {
+ pj_scan_syntax_err(scanner);
+ return;
+ }
+
+ while (PJ_SCAN_CHECK_EOF(s) && !pj_cs_match(spec, *s)) {
+ ++s;
+ }
+
+ pj_strset3(out, scanner->curptr, s);
+
+ scanner->col += (s - start);
+ scanner->curptr = s;
+
+ if (scanner->skip_ws) {
+ pj_scan_skip_whitespace(scanner);
+ }
+}
+
+
+PJ_DEF(void) pj_scan_get_until_ch( pj_scanner *scanner,
+ int until_char, pj_str_t *out)
+{
+ register char *s = scanner->curptr;
+ register char *end = scanner->end;
+ char *start = s;
+
+ PJ_CHECK_STACK();
+
+ if (pj_scan_is_eof(scanner)) {
+ pj_scan_syntax_err(scanner);
+ return;
+ }
+
+ while (PJ_SCAN_CHECK_EOF(s) && *s != until_char) {
+ ++s;
+ }
+
+ pj_strset3(out, scanner->curptr, s);
+
+ scanner->col += (s - start);
+ scanner->curptr = s;
+
+ if (scanner->skip_ws) {
+ pj_scan_skip_whitespace(scanner);
+ }
+}
+
+
+PJ_DEF(void) pj_scan_get_until_chr( pj_scanner *scanner,
+ const char *until_spec, pj_str_t *out)
+{
+ register char *s = scanner->curptr;
+ register char *end = scanner->end;
+ char *start = scanner->curptr;
+
+ PJ_CHECK_STACK();
+
+ if (pj_scan_is_eof(scanner)) {
+ pj_scan_syntax_err(scanner);
+ return;
+ }
+
+ while (PJ_SCAN_CHECK_EOF(s) && !strchr(until_spec, *s)) {
+ ++s;
+ }
+
+ pj_strset3(out, scanner->curptr, s);
+
+ scanner->col += (s - start);
+ scanner->curptr = s;
+
+ if (scanner->skip_ws) {
+ pj_scan_skip_whitespace(scanner);
+ }
+}
+
+PJ_DEF(void) pj_scan_advance_n( pj_scanner *scanner,
+ unsigned N, pj_bool_t skip_ws)
+{
+ char *start = scanner->curptr;
+
+ PJ_CHECK_STACK();
+
+ if (scanner->curptr + N > scanner->end) {
+ pj_scan_syntax_err(scanner);
+ return;
+ }
+
+ scanner->curptr += N;
+ scanner->col += (scanner->curptr - start);
+
+ if (skip_ws) {
+ pj_scan_skip_whitespace(scanner);
+ }
+}
+
+
+PJ_DEF(int) pj_scan_strcmp( pj_scanner *scanner, const char *s, int len)
+{
+ if (scanner->curptr + len > scanner->end) {
+ pj_scan_syntax_err(scanner);
+ return -1;
+ }
+ return strncmp(scanner->curptr, s, len);
+}
+
+
+PJ_DEF(int) pj_scan_stricmp( pj_scanner *scanner, const char *s, int len)
+{
+ if (scanner->curptr + len > scanner->end) {
+ pj_scan_syntax_err(scanner);
+ return -1;
+ }
+ return strnicmp(scanner->curptr, s, len);
+}
+
+
+PJ_DEF(void) pj_scan_save_state( pj_scanner *scanner, pj_scan_state *state)
+{
+ PJ_CHECK_STACK();
+
+ state->curptr = scanner->curptr;
+ state->line = scanner->line;
+ state->col = scanner->col;
+}
+
+
+PJ_DEF(void) pj_scan_restore_state( pj_scanner *scanner,
+ pj_scan_state *state)
+{
+ PJ_CHECK_STACK();
+
+ scanner->curptr = state->curptr;
+ scanner->line = state->line;
+ scanner->col = state->col;
+}
+
+
diff --git a/pjlib/src/pj/sock_bsd.c b/pjlib/src/pj/sock_bsd.c
new file mode 100644
index 00000000..c69b7e25
--- /dev/null
+++ b/pjlib/src/pj/sock_bsd.c
@@ -0,0 +1,572 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/sock_bsd.c 10 10/29/05 11:51a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/sock_bsd.c $
+ *
+ * 10 10/29/05 11:51a Bennylp
+ * Version 0.3-pre2.
+ *
+ * 9 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 8 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 7 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/sock.h>
+#include <pj/os.h>
+#include <pj/assert.h>
+#include <pj/string.h>
+#include <pj/compat/socket.h>
+#include <pj/addr_resolv.h>
+#include <pj/errno.h>
+
+/*
+ * Address families conversion.
+ * The values here are indexed based on pj_addr_family-0xFF00.
+ */
+const pj_uint16_t PJ_AF_UNIX = AF_UNIX;
+const pj_uint16_t PJ_AF_INET = AF_INET;
+const pj_uint16_t PJ_AF_INET6 = AF_INET6;
+#ifdef AF_PACKET
+const pj_uint16_t PJ_AF_PACKET = AF_PACKET;
+#else
+const pj_uint16_t PJ_AF_PACKET = 0xFFFF;
+#endif
+#ifdef AF_IRDA
+const pj_uint16_t PJ_AF_IRDA = AF_IRDA;
+#else
+const pj_uint16_t PJ_AF_IRDA = 0xFFFF;
+#endif
+
+/*
+ * Socket types conversion.
+ * The values here are indexed based on pj_sock_type-0xFF00
+ */
+const pj_uint16_t PJ_SOCK_STREAM = SOCK_STREAM;
+const pj_uint16_t PJ_SOCK_DGRAM = SOCK_DGRAM;
+const pj_uint16_t PJ_SOCK_RAW = SOCK_RAW;
+const pj_uint16_t PJ_SOCK_RDM = SOCK_RDM;
+
+/*
+ * Socket level values.
+ */
+const pj_uint16_t PJ_SOL_SOCKET = SOL_SOCKET;
+#ifdef SOL_IP
+const pj_uint16_t PJ_SOL_IP = SOL_IP;
+#else
+const pj_uint16_t PJ_SOL_IP = 0xFFFF;
+#endif /* SOL_IP */
+#if defined(SOL_TCP)
+const pj_uint16_t PJ_SOL_TCP = SOL_TCP;
+#elif defined(IPPROTO_TCP)
+const pj_uint16_t PJ_SOL_TCP = IPPROTO_TCP;
+#endif /* SOL_TCP */
+#ifdef SOL_UDP
+const pj_uint16_t PJ_SOL_UDP = SOL_UDP;
+#else
+const pj_uint16_t PJ_SOL_UDP = 0xFFFF;
+#endif
+#ifdef SOL_IPV6
+const pj_uint16_t PJ_SOL_IPV6 = SOL_IPV6;
+#else
+const pj_uint16_t PJ_SOL_IPV6 = 0xFFFF;
+#endif
+
+
+/*
+ * Convert 16-bit value from network byte order to host byte order.
+ */
+PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort)
+{
+ return ntohs(netshort);
+}
+
+/*
+ * Convert 16-bit value from host byte order to network byte order.
+ */
+PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort)
+{
+ return htons(hostshort);
+}
+
+/*
+ * Convert 32-bit value from network byte order to host byte order.
+ */
+PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong)
+{
+ return ntohl(netlong);
+}
+
+/*
+ * Convert 32-bit value from host byte order to network byte order.
+ */
+PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong)
+{
+ return htonl(hostlong);
+}
+
+/*
+ * Convert an Internet host address given in network byte order
+ * to string in standard numbers and dots notation.
+ */
+PJ_DEF(char*) pj_inet_ntoa(pj_in_addr inaddr)
+{
+ return inet_ntoa(*(struct in_addr*)&inaddr);
+}
+
+/*
+ * This function converts the Internet host address cp from the standard
+ * numbers-and-dots notation into binary data and stores it in the structure
+ * that inp points to.
+ */
+PJ_DEF(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp)
+{
+ char tempaddr[16];
+
+ /* Initialize output with PJ_INADDR_NONE.
+ * Some apps relies on this instead of the return value
+ * (and anyway the return value is quite confusing!)
+ */
+ inp->s_addr = PJ_INADDR_NONE;
+
+ /* Caution:
+ * this function might be called with cp->slen >= 16
+ * (i.e. when called with hostname to check if it's an IP addr).
+ */
+ PJ_ASSERT_RETURN(cp && cp->slen && inp, 0);
+ if (cp->slen >= 16) {
+ return 0;
+ }
+
+ pj_memcpy(tempaddr, cp->ptr, cp->slen);
+ tempaddr[cp->slen] = '\0';
+
+#if defined(PJ_SOCK_HAS_INET_ATON) && PJ_SOCK_HAS_INET_ATON != 0
+ return inet_aton(tempaddr, (struct in_addr*)inp);
+#else
+ inp->s_addr = inet_addr(tempaddr);
+ return inp->s_addr == PJ_INADDR_NONE ? 0 : 1;
+#endif
+}
+
+/*
+ * Convert address string with numbers and dots to binary IP address.
+ */
+PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp)
+{
+ pj_in_addr addr;
+
+ pj_inet_aton(cp, &addr);
+ return addr;
+}
+
+/*
+ * Set the IP address of an IP socket address from string address,
+ * with resolving the host if necessary. The string address may be in a
+ * standard numbers and dots notation or may be a hostname. If hostname
+ * is specified, then the function will resolve the host into the IP
+ * address.
+ */
+PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr,
+ const pj_str_t *str_addr)
+{
+ PJ_CHECK_STACK();
+
+ PJ_ASSERT_RETURN(str_addr && str_addr->slen < PJ_MAX_HOSTNAME,
+ (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL));
+
+ addr->sin_family = AF_INET;
+
+ if (str_addr && str_addr->slen) {
+ addr->sin_addr = pj_inet_addr(str_addr);
+ if (addr->sin_addr.s_addr == PJ_INADDR_NONE) {
+ pj_hostent he;
+ pj_status_t rc;
+
+ rc = pj_gethostbyname(str_addr, &he);
+ if (rc == 0) {
+ addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr;
+ } else {
+ addr->sin_addr.s_addr = PJ_INADDR_NONE;
+ return rc;
+ }
+ }
+
+ } else {
+ addr->sin_addr.s_addr = 0;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Set the IP address and port of an IP socket address.
+ * The string address may be in a standard numbers and dots notation or
+ * may be a hostname. If hostname is specified, then the function will
+ * resolve the host into the IP address.
+ */
+PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr,
+ const pj_str_t *str_addr,
+ pj_uint16_t port)
+{
+ PJ_ASSERT_RETURN(addr && str_addr,
+ (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL));
+
+ addr->sin_family = PJ_AF_INET;
+ pj_sockaddr_in_set_port(addr, port);
+ return pj_sockaddr_in_set_str_addr(addr, str_addr);
+}
+
+
+/*
+ * Get hostname.
+ */
+PJ_DEF(const pj_str_t*) pj_gethostname(void)
+{
+ static char buf[PJ_MAX_HOSTNAME];
+ static pj_str_t hostname;
+
+ PJ_CHECK_STACK();
+
+ if (hostname.ptr == NULL) {
+ hostname.ptr = buf;
+ if (gethostname(buf, sizeof(buf)) != 0) {
+ hostname.ptr[0] = '\0';
+ hostname.slen = 0;
+ } else {
+ hostname.slen = strlen(buf);
+ }
+ }
+ return &hostname;
+}
+
+/*
+ * Get first IP address associated with the hostname.
+ */
+PJ_DEF(pj_in_addr) pj_gethostaddr(void)
+{
+ pj_sockaddr_in addr;
+ const pj_str_t *hostname = pj_gethostname();
+
+ pj_sockaddr_in_set_str_addr(&addr, hostname);
+ return addr.sin_addr;
+}
+
+
+#if defined(PJ_WIN32)
+/*
+ * Create new socket/endpoint for communication and returns a descriptor.
+ */
+PJ_DEF(pj_status_t) pj_sock_socket(int af,
+ int type,
+ int proto,
+ pj_sock_t *sock)
+{
+ PJ_CHECK_STACK();
+
+ /* Sanity checks. */
+ PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL);
+ PJ_ASSERT_RETURN((unsigned)PJ_INVALID_SOCKET==INVALID_SOCKET,
+ (*sock=PJ_INVALID_SOCKET, PJ_EINVAL));
+
+ *sock = WSASocket(af, type, proto, NULL, 0, WSA_FLAG_OVERLAPPED);
+
+ if (*sock == PJ_INVALID_SOCKET)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+#else
+/*
+ * Create new socket/endpoint for communication and returns a descriptor.
+ */
+PJ_DEF(pj_status_t) pj_sock_socket(int af,
+ int type,
+ int proto,
+ pj_sock_t *sock)
+{
+
+ PJ_CHECK_STACK();
+
+ /* Sanity checks. */
+ PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL);
+ PJ_ASSERT_RETURN(PJ_INVALID_SOCKET==-1,
+ (*sock=PJ_INVALID_SOCKET, PJ_EINVAL));
+
+ *sock = socket(af, type, proto);
+ if (*sock == PJ_INVALID_SOCKET)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+#endif
+
+
+/*
+ * Bind socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sock,
+ const pj_sockaddr_t *addr,
+ int len)
+{
+ PJ_CHECK_STACK();
+
+ PJ_ASSERT_RETURN(addr && len > 0, PJ_EINVAL);
+
+ if (bind(sock, (struct sockaddr*)addr, len) != 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Bind socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sock,
+ pj_uint32_t addr32,
+ pj_uint16_t port)
+{
+ pj_sockaddr_in addr;
+
+ PJ_CHECK_STACK();
+
+ addr.sin_family = PJ_AF_INET;
+ addr.sin_addr.s_addr = pj_htonl(addr32);
+ addr.sin_port = pj_htons(port);
+
+ return pj_sock_bind(sock, &addr, sizeof(pj_sockaddr_in));
+}
+
+
+/*
+ * Close socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sock)
+{
+ int rc;
+
+ PJ_CHECK_STACK();
+#if defined(PJ_WIN32) && PJ_WIN32==1
+ rc = closesocket(sock);
+#else
+ rc = close(sock);
+#endif
+
+ if (rc != 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Get remote's name.
+ */
+PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sock,
+ pj_sockaddr_t *addr,
+ int *namelen)
+{
+ PJ_CHECK_STACK();
+ if (getpeername(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Get socket name.
+ */
+PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sock,
+ pj_sockaddr_t *addr,
+ int *namelen)
+{
+ PJ_CHECK_STACK();
+ if (getsockname(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Send data
+ */
+PJ_DEF(pj_status_t) pj_sock_send(pj_sock_t sock,
+ const void *buf,
+ pj_ssize_t *len,
+ unsigned flags)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(len, PJ_EINVAL);
+
+ *len = send(sock, (const char*)buf, *len, flags);
+
+ if (*len < 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Send data.
+ */
+PJ_DEF(pj_status_t) pj_sock_sendto(pj_sock_t sock,
+ const void *buf,
+ pj_ssize_t *len,
+ unsigned flags,
+ const pj_sockaddr_t *to,
+ int tolen)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(len, PJ_EINVAL);
+
+ *len = sendto(sock, (const char*)buf, *len, flags,
+ (const struct sockaddr*)to, tolen);
+
+ if (*len < 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Receive data.
+ */
+PJ_DEF(pj_status_t) pj_sock_recv(pj_sock_t sock,
+ void *buf,
+ pj_ssize_t *len,
+ unsigned flags)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(buf && len, PJ_EINVAL);
+
+ *len = recv(sock, (char*)buf, *len, flags);
+
+ if (*len < 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Receive data.
+ */
+PJ_DEF(pj_status_t) pj_sock_recvfrom(pj_sock_t sock,
+ void *buf,
+ pj_ssize_t *len,
+ unsigned flags,
+ pj_sockaddr_t *from,
+ int *fromlen)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(buf && len, PJ_EINVAL);
+ PJ_ASSERT_RETURN(from && fromlen, (*len=-1, PJ_EINVAL));
+
+ *len = recvfrom(sock, (char*)buf, *len, flags,
+ (struct sockaddr*)from, (socklen_t*)fromlen);
+
+ if (*len < 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Get socket option.
+ */
+PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sock,
+ int level,
+ int optname,
+ void *optval,
+ int *optlen)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(optval && optlen, PJ_EINVAL);
+
+ if (getsockopt(sock, level, optname, (char*)optval, (socklen_t*)optlen)!=0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Set socket option.
+ */
+PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sock,
+ int level,
+ int optname,
+ const void *optval,
+ int optlen)
+{
+ PJ_CHECK_STACK();
+ if (setsockopt(sock, level, optname, (const char*)optval, optlen) != 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Shutdown socket.
+ */
+#if PJ_HAS_TCP
+PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sock,
+ int how)
+{
+ PJ_CHECK_STACK();
+ if (shutdown(sock, how) != 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Start listening to incoming connections.
+ */
+PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sock,
+ int backlog)
+{
+ PJ_CHECK_STACK();
+ if (listen(sock, backlog) != 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Connect socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sock,
+ const pj_sockaddr_t *addr,
+ int namelen)
+{
+ PJ_CHECK_STACK();
+ if (connect(sock, (struct sockaddr*)addr, namelen) != 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Accept incoming connections
+ */
+PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t serverfd,
+ pj_sock_t *newsock,
+ pj_sockaddr_t *addr,
+ int *addrlen)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(newsock != NULL, PJ_EINVAL);
+
+ *newsock = accept(serverfd, (struct sockaddr*)addr, (socklen_t*)addrlen);
+ if (*newsock==PJ_INVALID_SOCKET)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+#endif /* PJ_HAS_TCP */
+
+
diff --git a/pjlib/src/pj/sock_linux_kernel.c b/pjlib/src/pj/sock_linux_kernel.c
new file mode 100644
index 00000000..76bc7bd8
--- /dev/null
+++ b/pjlib/src/pj/sock_linux_kernel.c
@@ -0,0 +1,749 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/sock_linux_kernel.c 4 10/29/05 11:51a Bennylp $ */
+/*
+ * $Log: /pjproject-0.3/pjlib/src/pj/sock_linux_kernel.c $
+ *
+ * 4 10/29/05 11:51a Bennylp
+ * Version 0.3-pre2.
+ *
+ * 3 10/20/05 9:19a Bennylp
+ * Updated with new API convention (error code)
+ *
+ * 2 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 1 10/05/05 4:43p Bennylp
+ * Created.
+ *
+ */
+#include <pj/sock.h>
+#include <pj/assert.h>
+#include <pj/string.h> /* pj_memcpy() */
+#include <pj/os.h> /* PJ_CHECK_STACK() */
+#include <pj/addr_resolv.h> /* pj_gethostbyname() */
+#include <pj/ctype.h>
+#include <pj/compat/sprintf.h>
+#include <pj/log.h>
+#include <pj/errno.h>
+
+/* Linux kernel specific. */
+#include <linux/socket.h>
+#include <linux/net.h>
+//#include <net/sock.h>
+#include <linux/security.h>
+#include <linux/syscalls.h> /* sys_xxx() */
+#include <asm/ioctls.h> /* FIONBIO */
+#include <linux/utsname.h> /* for pj_gethostname() */
+
+/*
+ * Address families conversion.
+ * The values here are indexed based on pj_addr_family-0xFF00.
+ */
+const pj_uint16_t PJ_AF_UNIX = AF_UNIX;
+const pj_uint16_t PJ_AF_INET = AF_INET;
+const pj_uint16_t PJ_AF_INET6 = AF_INET6;
+#ifdef AF_PACKET
+const pj_uint16_t PJ_AF_PACKET = AF_PACKET;
+#else
+# error "AF_PACKET undeclared!"
+#endif
+#ifdef AF_IRDA
+const pj_uint16_t PJ_AF_IRDA = AF_IRDA;
+#else
+# error "AF_IRDA undeclared!"
+#endif
+
+/*
+ * Socket types conversion.
+ * The values here are indexed based on pj_sock_type-0xFF00
+ */
+const pj_uint16_t PJ_SOCK_STREAM= SOCK_STREAM;
+const pj_uint16_t PJ_SOCK_DGRAM = SOCK_DGRAM;
+const pj_uint16_t PJ_SOCK_RAW = SOCK_RAW;
+const pj_uint16_t PJ_SOCK_RDM = SOCK_RDM;
+
+/*
+ * Socket level values.
+ */
+const pj_uint16_t PJ_SOL_SOCKET = SOL_SOCKET;
+#ifdef SOL_IP
+const pj_uint16_t PJ_SOL_IP = SOL_IP;
+#else
+# error "SOL_IP undeclared!"
+#endif /* SOL_IP */
+#if defined(SOL_TCP)
+const pj_uint16_t PJ_SOL_TCP = SOL_TCP;
+#else
+# error "SOL_TCP undeclared!"
+#endif /* SOL_TCP */
+#ifdef SOL_UDP
+const pj_uint16_t PJ_SOL_UDP = SOL_UDP;
+#else
+# error "SOL_UDP undeclared!"
+#endif
+#ifdef SOL_IPV6
+const pj_uint16_t PJ_SOL_IPV6 = SOL_IPV6;
+#else
+# error "SOL_IPV6 undeclared!"
+#endif
+
+/*
+ * Convert 16-bit value from network byte order to host byte order.
+ */
+PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort)
+{
+ return ntohs(netshort);
+}
+
+/*
+ * Convert 16-bit value from host byte order to network byte order.
+ */
+PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort)
+{
+ return htons(hostshort);
+}
+
+/*
+ * Convert 32-bit value from network byte order to host byte order.
+ */
+PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong)
+{
+ return ntohl(netlong);
+}
+
+/*
+ * Convert 32-bit value from host byte order to network byte order.
+ */
+PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong)
+{
+ return htonl(hostlong);
+}
+
+/*
+ * Convert an Internet host address given in network byte order
+ * to string in standard numbers and dots notation.
+ */
+PJ_DEF(char*) pj_inet_ntoa(pj_in_addr in)
+{
+#define UC(b) (((int)b)&0xff)
+ static char b[18];
+ char *p;
+
+ p = (char *)&in;
+ pj_snprintf(b, sizeof(b), "%d.%d.%d.%d",
+ UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3]));
+
+ return b;
+}
+
+/*
+ * This function converts the Internet host address ccp from the standard
+ * numbers-and-dots notation into binary data and stores it in the structure
+ * that inp points to.
+ */
+PJ_DEF(int) pj_inet_aton(const pj_str_t *ccp, struct pj_in_addr *addr)
+{
+ pj_uint32_t val;
+ int base, n;
+ char c;
+ unsigned parts[4];
+ unsigned *pp = parts;
+ char cp_copy[18];
+ char *cp = cp_copy;
+
+ addr->s_addr = PJ_INADDR_NONE;
+
+ if (ccp->slen > 15) return 0;
+
+ pj_memcpy(cp, ccp->ptr, ccp->slen);
+ cp[ccp->slen] = '\0';
+
+ c = *cp;
+ for (;;) {
+ /*
+ * Collect number up to ``.''.
+ * Values are specified as for C:
+ * 0x=hex, 0=octal, isdigit=decimal.
+ */
+ if (!pj_isdigit((int)c))
+ return (0);
+ val = 0; base = 10;
+ if (c == '0') {
+ c = *++cp;
+ if (c == 'x' || c == 'X')
+ base = 16, c = *++cp;
+ else
+ base = 8;
+ }
+
+ for (;;) {
+ if (pj_isascii((int)c) && pj_isdigit((int)c)) {
+ val = (val * base) + (c - '0');
+ c = *++cp;
+ } else if (base==16 && pj_isascii((int)c) && pj_isxdigit((int)c)) {
+ val = (val << 4) |
+ (c + 10 - (pj_islower((int)c) ? 'a' : 'A'));
+ c = *++cp;
+ } else
+ break;
+ }
+
+ if (c == '.') {
+ /*
+ * Internet format:
+ * a.b.c.d
+ * a.b.c (with c treated as 16 bits)
+ * a.b (with b treated as 24 bits)
+ */
+ if (pp >= parts + 3)
+ return (0);
+ *pp++ = val;
+ c = *++cp;
+ } else
+ break;
+ }
+
+ /*
+ * Check for trailing characters.
+ */
+ if (c != '\0' && (!pj_isascii((int)c) || !pj_isspace((int)c)))
+ return (0);
+ /*
+ * Concoct the address according to
+ * the number of parts specified.
+ */
+ n = pp - parts + 1;
+ switch (n) {
+ case 0:
+ return (0); /* initial nondigit */
+ case 1: /* a -- 32 bits */
+ break;
+ case 2: /* a.b -- 8.24 bits */
+ if (val > 0xffffff)
+ return (0);
+ val |= parts[0] << 24;
+ break;
+ case 3: /* a.b.c -- 8.8.16 bits */
+ if (val > 0xffff)
+ return (0);
+ val |= (parts[0] << 24) | (parts[1] << 16);
+ break;
+ case 4: /* a.b.c.d -- 8.8.8.8 bits */
+ if (val > 0xff)
+ return (0);
+ val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+ break;
+ }
+
+ if (addr)
+ addr->s_addr = pj_htonl(val);
+ return (1);
+}
+
+/*
+ * Convert address string with numbers and dots to binary IP address.
+ */
+PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp)
+{
+ pj_in_addr addr;
+ pj_inet_aton(cp, &addr);
+ return addr;
+}
+
+/*
+ * Set the IP address of an IP socket address from string address,
+ * with resolving the host if necessary. The string address may be in a
+ * standard numbers and dots notation or may be a hostname. If hostname
+ * is specified, then the function will resolve the host into the IP
+ * address.
+ */
+PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr,
+ const pj_str_t *str_addr)
+{
+ PJ_CHECK_STACK();
+
+ pj_assert(str_addr && str_addr->slen < PJ_MAX_HOSTNAME);
+
+ addr->sin_family = AF_INET;
+
+ if (str_addr && str_addr->slen) {
+ addr->sin_addr = pj_inet_addr(str_addr);
+ if (addr->sin_addr.s_addr == PJ_INADDR_NONE) {
+ pj_hostent he;
+ if (pj_gethostbyname(str_addr, &he) == 0) {
+ addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr;
+ } else {
+ addr->sin_addr.s_addr = PJ_INADDR_NONE;
+ return -1;
+ }
+ }
+
+ } else {
+ addr->sin_addr.s_addr = 0;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Set the IP address and port of an IP socket address.
+ * The string address may be in a standard numbers and dots notation or
+ * may be a hostname. If hostname is specified, then the function will
+ * resolve the host into the IP address.
+ */
+PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr,
+ const pj_str_t *str_addr,
+ pj_uint16_t port)
+{
+ pj_assert(addr && str_addr);
+
+ addr->sin_family = PJ_AF_INET;
+ pj_sockaddr_in_set_port(addr, port);
+ return pj_sockaddr_in_set_str_addr(addr, str_addr);
+}
+
+
+/*
+ * Get hostname.
+ */
+PJ_DEF(const pj_str_t*) pj_gethostname(void)
+{
+ static char buf[PJ_MAX_HOSTNAME];
+ static pj_str_t hostname;
+
+ PJ_CHECK_STACK();
+
+ if (hostname.ptr == NULL) {
+ hostname.ptr = buf;
+ down_read(&uts_sem);
+ hostname.slen = strlen(system_utsname.nodename);
+ if (hostname.slen > PJ_MAX_HOSTNAME) {
+ hostname.ptr[0] = '\0';
+ hostname.slen = 0;
+ } else {
+ pj_memcpy(hostname.ptr, system_utsname.nodename, hostname.slen);
+ }
+ up_read(&uts_sem);
+ }
+ return &hostname;
+}
+
+/*
+ * Get first IP address associated with the hostname.
+ */
+PJ_DEF(pj_in_addr) pj_gethostaddr(void)
+{
+ pj_sockaddr_in addr;
+ const pj_str_t *hostname = pj_gethostname();
+
+ pj_sockaddr_in_set_str_addr(&addr, hostname);
+ return addr.sin_addr;
+}
+
+
+/*
+ * Create new socket/endpoint for communication and returns a descriptor.
+ */
+PJ_DEF(pj_status_t) pj_sock_socket(int af, int type, int proto,
+ pj_sock_t *sock_fd)
+{
+ long result;
+
+ PJ_CHECK_STACK();
+
+ /* Sanity checks. */
+ PJ_ASSERT_RETURN(PJ_INVALID_SOCKET == -1 && sock_fd != NULL, PJ_EINVAL);
+
+ /* Initialize returned socket */
+ *sock_fd = PJ_INVALID_SOCKET;
+
+ /* Create socket. */
+ result = sys_socket(af, type, proto);
+ if (result < 0) {
+ return PJ_RETURN_OS_ERROR((-result));
+ }
+
+ *sock_fd = result;
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Bind socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sockfd,
+ const pj_sockaddr_t *addr,
+ int len)
+{
+ long err;
+ mm_segment_t oldfs;
+
+ PJ_CHECK_STACK();
+
+ PJ_ASSERT_RETURN(addr!=NULL && len >= sizeof(struct pj_sockaddr),
+ PJ_EINVAL);
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ err = sys_bind(sockfd, (struct sockaddr*)addr, len);
+
+ set_fs(oldfs);
+
+ if (err)
+ return PJ_RETURN_OS_ERROR(-err);
+ else
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Bind socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sockfd,
+ pj_uint32_t addr32,
+ pj_uint16_t port)
+{
+ pj_sockaddr_in addr;
+
+ PJ_CHECK_STACK();
+
+ addr.sin_family = PJ_AF_INET;
+ addr.sin_addr.s_addr = pj_htonl(addr32);
+ addr.sin_port = pj_htons(port);
+
+ return pj_sock_bind(sockfd, &addr, sizeof(pj_sockaddr_in));
+}
+
+/*
+ * Close socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sockfd)
+{
+ long err;
+
+ err = sys_close(sockfd);
+
+ if (err != 0)
+ return PJ_RETURN_OS_ERROR(-err);
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Get remote's name.
+ */
+PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sockfd,
+ pj_sockaddr_t *addr,
+ int *namelen)
+{
+ mm_segment_t oldfs;
+ long err;
+
+ PJ_CHECK_STACK();
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ err = sys_getpeername( sockfd, addr, namelen);
+
+ set_fs(oldfs);
+
+ if (err)
+ return PJ_RETURN_OS_ERROR(-err);
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Get socket name.
+ */
+PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sockfd,
+ pj_sockaddr_t *addr,
+ int *namelen)
+{
+ mm_segment_t oldfs;
+ int err;
+
+ PJ_CHECK_STACK();
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ err = sys_getsockname( sockfd, addr, namelen );
+
+ set_fs(oldfs);
+
+ if (err)
+ return PJ_RETURN_OS_ERROR(-err);
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Send data
+ */
+PJ_DEF(pj_status_t) pj_sock_send( pj_sock_t sockfd,
+ const void *buf,
+ pj_ssize_t *len,
+ unsigned flags)
+{
+ return pj_sock_sendto(sockfd, buf, len, flags, NULL, 0);
+}
+
+
+/*
+ * Send data.
+ */
+PJ_DEF(pj_status_t) pj_sock_sendto( pj_sock_t sockfd,
+ const void *buff,
+ pj_ssize_t *len,
+ unsigned flags,
+ const pj_sockaddr_t *addr,
+ int addr_len)
+{
+ long err;
+ mm_segment_t oldfs;
+
+ PJ_CHECK_STACK();
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ err = *len = sys_sendto( sockfd, (void*)buff, *len, flags,
+ (void*)addr, addr_len );
+
+ set_fs(oldfs);
+
+ if (err >= 0) {
+ return PJ_SUCCESS;
+ }
+ else {
+ return PJ_RETURN_OS_ERROR(-err);
+ }
+}
+
+/*
+ * Receive data.
+ */
+PJ_DEF(pj_status_t) pj_sock_recv( pj_sock_t sockfd,
+ void *buf,
+ pj_ssize_t *len,
+ unsigned flags)
+{
+ return pj_sock_recvfrom(sockfd, buf, len, flags, NULL, NULL);
+}
+
+/*
+ * Receive data.
+ */
+PJ_DEF(pj_status_t) pj_sock_recvfrom( pj_sock_t sockfd,
+ void *buff,
+ pj_ssize_t *size,
+ unsigned flags,
+ pj_sockaddr_t *from,
+ int *fromlen)
+{
+ mm_segment_t oldfs;
+ long err;
+
+ PJ_CHECK_STACK();
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ err = *size = sys_recvfrom( sockfd, buff, *size, flags, from, fromlen);
+
+ set_fs(oldfs);
+
+ if (err >= 0) {
+ return PJ_SUCCESS;
+ }
+ else {
+ return PJ_RETURN_OS_ERROR(-err);
+ }
+}
+
+/*
+ * Get socket option.
+ */
+PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sockfd,
+ int level,
+ int optname,
+ void *optval,
+ int *optlen)
+{
+ mm_segment_t oldfs;
+ long err;
+
+ PJ_CHECK_STACK();
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ err = sys_getsockopt( sockfd, level, optname, optval, optlen);
+
+ set_fs(oldfs);
+
+ if (err)
+ return PJ_RETURN_OS_ERROR(-err);
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Set socket option.
+ */
+PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sockfd,
+ int level,
+ int optname,
+ const void *optval,
+ int optlen)
+{
+ long err;
+ mm_segment_t oldfs;
+
+ PJ_CHECK_STACK();
+
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ err = sys_setsockopt( sockfd, level, optname, (void*)optval, optlen);
+
+ set_fs(oldfs);
+
+ if (err)
+ return PJ_RETURN_OS_ERROR(-err);
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Shutdown socket.
+ */
+#if PJ_HAS_TCP
+PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sockfd,
+ int how)
+{
+ long err;
+
+ PJ_CHECK_STACK();
+
+ err = sys_shutdown(sockfd, how);
+
+ if (err)
+ return PJ_RETURN_OS_ERROR(-err);
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Start listening to incoming connections.
+ */
+PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sockfd,
+ int backlog)
+{
+ long err;
+
+ PJ_CHECK_STACK();
+
+ err = sys_listen( sockfd, backlog );
+
+ if (err)
+ return PJ_RETURN_OS_ERROR(-err);
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Connect socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sockfd,
+ const pj_sockaddr_t *addr,
+ int namelen)
+{
+ long err;
+ mm_segment_t oldfs;
+
+ PJ_CHECK_STACK();
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ err = sys_connect( sockfd, (void*)addr, namelen );
+
+ set_fs(oldfs);
+
+ if (err)
+ return PJ_RETURN_OS_ERROR(-err);
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Accept incoming connections
+ */
+PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t sockfd,
+ pj_sock_t *newsockfd,
+ pj_sockaddr_t *addr,
+ int *addrlen)
+{
+ long err;
+
+ PJ_CHECK_STACK();
+
+ PJ_ASSERT_RETURN(newsockfd != NULL, PJ_EINVAL);
+
+ err = sys_accept( sockfd, addr, addrlen);
+
+ if (err < 0) {
+ *newsockfd = PJ_INVALID_SOCKET;
+ return PJ_RETURN_OS_ERROR(-err);
+ }
+ else {
+ *newsockfd = err;
+ return PJ_SUCCESS;
+ }
+}
+#endif /* PJ_HAS_TCP */
+
+
+
+/*
+ * Permission to steal inet_ntoa() and inet_aton() as long as this notice below
+ * is included:
+ */
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
diff --git a/pjlib/src/pj/sock_select.c b/pjlib/src/pj/sock_select.c
new file mode 100644
index 00000000..49fa0116
--- /dev/null
+++ b/pjlib/src/pj/sock_select.c
@@ -0,0 +1,101 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/sock_select.c 4 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/sock_select.c $
+ *
+ * 4 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 3 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 2 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ * 1 9/15/05 8:40p Bennylp
+ * Created.
+ */
+#include <pj/sock_select.h>
+#include <pj/compat/socket.h>
+#include <pj/os.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+
+
+#ifdef _MSC_VER
+# pragma warning(disable: 4018) // Signed/unsigned mismatch in FD_*
+#endif
+
+#define PART_FDSET(p_fdsetp) ((fd_set*)&p_fdsetp->data[1])
+#define PART_COUNT(p_fdsetp) (p_fdsetp->data[0])
+
+PJ_DEF(void) PJ_FD_ZERO(pj_fd_set_t *fdsetp)
+{
+ PJ_CHECK_STACK();
+ pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set));
+
+ FD_ZERO(PART_FDSET(fdsetp));
+ PART_COUNT(fdsetp) = 0;
+}
+
+
+PJ_DEF(void) PJ_FD_SET(pj_sock_t fd, pj_fd_set_t *fdsetp)
+{
+ PJ_CHECK_STACK();
+ pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set));
+
+ if (!PJ_FD_ISSET(fd, fdsetp))
+ ++PART_COUNT(fdsetp);
+ FD_SET(fd, PART_FDSET(fdsetp));
+}
+
+
+PJ_DEF(void) PJ_FD_CLR(pj_sock_t fd, pj_fd_set_t *fdsetp)
+{
+ PJ_CHECK_STACK();
+ pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set));
+
+ if (PJ_FD_ISSET(fd, fdsetp))
+ --PART_COUNT(fdsetp);
+ FD_CLR(fd, PART_FDSET(fdsetp));
+}
+
+
+PJ_DEF(pj_bool_t) PJ_FD_ISSET(pj_sock_t fd, const pj_fd_set_t *fdsetp)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set),
+ 0);
+
+ return FD_ISSET(fd, PART_FDSET(fdsetp));
+}
+
+PJ_DEF(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp)
+{
+ return PART_COUNT(fdsetp);
+}
+
+PJ_DEF(int) pj_sock_select( int n,
+ pj_fd_set_t *readfds,
+ pj_fd_set_t *writefds,
+ pj_fd_set_t *exceptfds,
+ const pj_time_val *timeout)
+{
+ struct timeval os_timeout, *p_os_timeout;
+
+ PJ_CHECK_STACK();
+
+ PJ_ASSERT_RETURN(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set),
+ PJ_EBUG);
+
+ if (timeout) {
+ os_timeout.tv_sec = timeout->sec;
+ os_timeout.tv_usec = timeout->msec * 1000;
+ p_os_timeout = &os_timeout;
+ } else {
+ p_os_timeout = NULL;
+ }
+
+ return select(n, PART_FDSET(readfds), PART_FDSET(writefds),
+ PART_FDSET(exceptfds), p_os_timeout);
+}
+
diff --git a/pjlib/src/pj/string.c b/pjlib/src/pj/string.c
new file mode 100644
index 00000000..c3f1b5ff
--- /dev/null
+++ b/pjlib/src/pj/string.c
@@ -0,0 +1,124 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/string.c 9 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/string.c $
+ *
+ * 9 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 8 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/string.h>
+#include <pj/pool.h>
+#include <pj/ctype.h>
+#include <pj/rand.h>
+#include <pj/os.h>
+
+#if PJ_FUNCTIONS_ARE_INLINED==0
+# include <pj/string_i.h>
+#endif
+
+
+static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+PJ_DEF(pj_str_t*) pj_strltrim( pj_str_t *str )
+{
+ register char *p = str->ptr;
+ while (pj_isspace(*p))
+ ++p;
+ str->slen -= (p - str->ptr);
+ str->ptr = p;
+ return str;
+}
+
+PJ_DEF(pj_str_t*) pj_strrtrim( pj_str_t *str )
+{
+ char *end = str->ptr + str->slen;
+ register char *p = end - 1;
+ while (p >= str->ptr && pj_isspace(*p))
+ --p;
+ str->slen -= ((end - p) - 1);
+ return str;
+}
+
+PJ_INLINE(void) pj_val_to_hex_digit(unsigned value, char *p)
+{
+ *p++ = hex[ (value & 0xF0) >> 4 ];
+ *p++ = hex[ (value & 0x0F) ];
+}
+
+PJ_DEF(char*) pj_create_random_string(char *str, pj_size_t len)
+{
+ unsigned i;
+ char *p = str;
+
+ PJ_CHECK_STACK();
+
+ for (i=0; i<len/8; ++i) {
+ unsigned val = pj_rand();
+ pj_val_to_hex_digit( (val & 0xFF000000) >> 24, p+0 );
+ pj_val_to_hex_digit( (val & 0x00FF0000) >> 16, p+2 );
+ pj_val_to_hex_digit( (val & 0x0000FF00) >> 8, p+4 );
+ pj_val_to_hex_digit( (val & 0x000000FF) >> 0, p+6 );
+ p += 8;
+ }
+ for (i=i * 8; i<len; ++i) {
+ *p++ = hex[ pj_rand() & 0x0F ];
+ }
+ return str;
+}
+
+
+PJ_DEF(unsigned long) pj_strtoul(const pj_str_t *str)
+{
+ unsigned long value;
+ unsigned i;
+
+ PJ_CHECK_STACK();
+
+ value = 0;
+ for (i=0; i<(unsigned)str->slen; ++i) {
+ value = value * 10 + (str->ptr[i] - '0');
+ }
+ return value;
+}
+
+PJ_DEF(int) pj_utoa(unsigned long val, char *buf)
+{
+ return pj_utoa_pad(val, buf, 0, 0);
+}
+
+PJ_DEF(int) pj_utoa_pad( unsigned long val, char *buf, int min_dig, int pad)
+{
+ char *p;
+ int len;
+
+ PJ_CHECK_STACK();
+
+ p = buf;
+ do {
+ unsigned long digval = (unsigned long) (val % 10);
+ val /= 10;
+ *p++ = (char) (digval + '0');
+ } while (val > 0);
+
+ len = p-buf;
+ while (len < min_dig) {
+ *p++ = (char)pad;
+ ++len;
+ }
+ *p-- = '\0';
+
+ do {
+ char temp = *p;
+ *p = *buf;
+ *buf = temp;
+ --p;
+ ++buf;
+ } while (buf < p);
+
+ return len;
+}
+
diff --git a/pjlib/src/pj/stun.c b/pjlib/src/pj/stun.c
new file mode 100644
index 00000000..efd43560
--- /dev/null
+++ b/pjlib/src/pj/stun.c
@@ -0,0 +1,118 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/stun.c 6 9/17/05 10:37a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/stun.c $
+ *
+ * 6 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/stun.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/sock.h>
+#include <pj/os.h>
+
+#define THIS_FILE "stun"
+
+PJ_DEF(pj_status_t) pj_stun_create_bind_req( pj_pool_t *pool,
+ void **msg, pj_size_t *len,
+ pj_uint32_t id_hi,
+ pj_uint32_t id_lo)
+{
+ pj_stun_msg_hdr *hdr;
+
+ PJ_CHECK_STACK();
+
+ PJ_LOG(5,(THIS_FILE, "pj_stun_create_bind_req"));
+
+ hdr = pj_pool_calloc(pool, 1, sizeof(pj_stun_msg_hdr));
+ if (!hdr) {
+ PJ_LOG(5,(THIS_FILE, "Error allocating memory!"));
+ return -1;
+ }
+
+ hdr->type = pj_htons(PJ_STUN_BINDING_REQUEST);
+ hdr->tsx[2] = pj_htonl(id_hi);
+ hdr->tsx[3] = pj_htonl(id_lo);
+ *msg = hdr;
+ *len = sizeof(pj_stun_msg_hdr);
+
+ return 0;
+}
+
+PJ_DEF(pj_status_t) pj_stun_parse_msg( void *buf, pj_size_t len,
+ pj_stun_msg *msg)
+{
+ pj_uint16_t msg_type, msg_len;
+ char *p_attr;
+
+ PJ_CHECK_STACK();
+
+ PJ_LOG(5,(THIS_FILE, "pj_stun_parse_msg %p, len=%d", buf, len));
+
+ msg->hdr = (pj_stun_msg_hdr*)buf;
+ msg_type = pj_ntohs(msg->hdr->type);
+
+ switch (msg_type) {
+ case PJ_STUN_BINDING_REQUEST:
+ case PJ_STUN_BINDING_RESPONSE:
+ case PJ_STUN_BINDING_ERROR_RESPONSE:
+ case PJ_STUN_SHARED_SECRET_REQUEST:
+ case PJ_STUN_SHARED_SECRET_RESPONSE:
+ case PJ_STUN_SHARED_SECRET_ERROR_RESPONSE:
+ break;
+ default:
+ PJ_LOG(5,(THIS_FILE, "Error: unknown msg type %d", msg_type));
+ return -1;
+ }
+
+ msg_len = pj_ntohs(msg->hdr->length);
+ if (msg_len != len - sizeof(pj_stun_msg_hdr)) {
+ PJ_LOG(5,(THIS_FILE, "Error: invalid msg_len %d (expecting %d)",
+ msg_len, len - sizeof(pj_stun_msg_hdr)));
+ return -1;
+ }
+
+ msg->attr_count = 0;
+ p_attr = (char*)buf + sizeof(pj_stun_msg_hdr);
+
+ while (msg_len > 0) {
+ pj_stun_attr_hdr **attr = &msg->attr[msg->attr_count];
+ pj_uint32_t len;
+
+ *attr = (pj_stun_attr_hdr*)p_attr;
+ len = pj_ntohs((pj_uint16_t) ((*attr)->length)) + sizeof(pj_stun_attr_hdr);
+
+ if (msg_len < len) {
+ PJ_LOG(5,(THIS_FILE, "Error: length mismatch in attr %d",
+ msg->attr_count));
+ return -1;
+ }
+
+ if (pj_ntohs((*attr)->type) > PJ_STUN_ATTR_REFLECTED_FORM) {
+ PJ_LOG(5,(THIS_FILE, "Error: invalid attr type %d in attr %d",
+ pj_ntohs((*attr)->type), msg->attr_count));
+ return -1;
+ }
+
+ msg_len = (pj_uint16_t)(msg_len - len);
+ p_attr += len;
+ ++msg->attr_count;
+ }
+
+ return 0;
+}
+
+PJ_DEF(void*) pj_stun_msg_find_attr( pj_stun_msg *msg, pj_stun_attr_type t)
+{
+ int i;
+
+ PJ_CHECK_STACK();
+
+ for (i=0; i<msg->attr_count; ++i) {
+ pj_stun_attr_hdr *attr = msg->attr[i];
+ if (pj_ntohs(attr->type) == t)
+ return attr;
+ }
+
+ return 0;
+}
diff --git a/pjlib/src/pj/stun_client.c b/pjlib/src/pj/stun_client.c
new file mode 100644
index 00000000..c35a8541
--- /dev/null
+++ b/pjlib/src/pj/stun_client.c
@@ -0,0 +1,270 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/stun_client.c 6 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/stun_client.c $
+ *
+ * 6 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 5 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/stun.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/os.h>
+#include <pj/sock_select.h>
+
+enum { MAX_REQUEST = 3 };
+static int stun_timer[] = {1600, 1600, 1600 };
+
+#define THIS_FILE "stunclient"
+#define LOG_ADDR(addr) pj_inet_ntoa(addr.sin_addr), pj_ntohs(addr.sin_port)
+
+
+PJ_DECL(pj_status_t) pj_stun_get_mapped_addr( pj_pool_factory *pf,
+ int sock_cnt, pj_sock_t sock[],
+ const pj_str_t *srv1, int port1,
+ const pj_str_t *srv2, int port2,
+ pj_sockaddr_in mapped_addr[])
+{
+ pj_sockaddr_in srv_addr[2];
+ int i, j, rc, send_cnt = 0;
+ pj_pool_t *pool;
+ struct {
+ struct {
+ pj_uint32_t mapped_addr;
+ pj_uint32_t mapped_port;
+ } srv[2];
+ } *rec;
+ void *out_msg;
+ pj_size_t out_msg_len;
+ int wait_resp = 0;
+ int mapped_status = 0;
+
+ PJ_CHECK_STACK();
+
+ /* Create pool. */
+ pool = pj_pool_create(pf, "stun%p", 1024, 1024, NULL);
+ if (!pool) {
+ mapped_status = PJ_STUN_ERR_MEMORY;
+ return -1;
+ }
+
+ /* Allocate client records */
+ rec = pj_pool_calloc(pool, sock_cnt, sizeof(*rec));
+ if (!rec) {
+ mapped_status = PJ_STUN_ERR_MEMORY;
+ goto on_error;
+ }
+
+ /* Create the outgoing BIND REQUEST message template */
+ rc = pj_stun_create_bind_req( pool, &out_msg, &out_msg_len, 0, 0);
+ if (rc != 0) {
+ mapped_status = -1;
+ goto on_error;
+ }
+
+ /* Resolve servers. */
+ if (pj_sockaddr_in_init(&srv_addr[0], srv1, (pj_uint16_t)port1) != 0) {
+ mapped_status = PJ_STUN_ERR_RESOLVE;
+ goto on_error;
+ }
+ if (pj_sockaddr_in_init(&srv_addr[1], srv2, (pj_uint16_t)port2) != 0) {
+ mapped_status = PJ_STUN_ERR_RESOLVE;
+ goto on_error;
+ }
+
+ /* Init mapped addresses to zero */
+ pj_memset(mapped_addr, 0, sock_cnt * sizeof(pj_sockaddr_in));
+
+ /* Main retransmission loop. */
+ for (send_cnt=0; send_cnt<MAX_REQUEST; ++send_cnt) {
+ pj_time_val next_tx, now;
+ pj_fd_set_t r;
+ int select_rc;
+
+ PJ_LOG(4,(THIS_FILE, "STUN retransmit %d, wait_resp=%d",
+ send_cnt, wait_resp));
+
+ PJ_FD_ZERO(&r);
+
+ /* Send messages to servers that has not given us response. */
+ for (i=0; i<sock_cnt && mapped_status==0; ++i) {
+ for (j=0; j<2 && mapped_status==0; ++j) {
+ pj_stun_msg_hdr *msg_hdr = out_msg;
+ pj_ssize_t sent_len;
+
+ if (rec[i].srv[j].mapped_port != 0)
+ continue;
+
+ /* Modify message so that we can distinguish response. */
+ msg_hdr->tsx[2] = pj_htonl(i);
+ msg_hdr->tsx[3] = pj_htonl(j);
+
+ /* Send! */
+ sent_len = out_msg_len;
+ rc = pj_sock_sendto(sock[i], out_msg, &sent_len, 0,
+ (pj_sockaddr_t*)&srv_addr[j],
+ sizeof(pj_sockaddr_in));
+ if (sent_len != (int)out_msg_len) {
+ PJ_LOG(4,(THIS_FILE,
+ "Error sending STUN request to %s:%d",
+ LOG_ADDR(srv_addr[j])));
+ mapped_status = PJ_STUN_ERR_TRANSPORT;
+ } else {
+ ++wait_resp;
+ }
+ }
+ }
+
+ /* All requests sent.
+ * The loop below will wait for responses until all responses have
+ * been received (i.e. wait_resp==0) or timeout occurs, which then
+ * we'll go to the next retransmission iteration.
+ */
+
+ /* Calculate time of next retransmission. */
+ pj_gettimeofday(&next_tx);
+ next_tx.sec += (stun_timer[send_cnt]/1000);
+ next_tx.msec += (stun_timer[send_cnt]%1000);
+ pj_time_val_normalize(&next_tx);
+
+ for (pj_gettimeofday(&now), select_rc=1;
+ mapped_status==0 && select_rc==1 && wait_resp>0 && PJ_TIME_VAL_LT(now, next_tx);
+ pj_gettimeofday(&now))
+ {
+ pj_time_val timeout;
+
+ timeout = next_tx;
+ PJ_TIME_VAL_SUB(timeout, now);
+
+ for (i=0; i<sock_cnt; ++i) {
+ PJ_FD_SET(sock[i], &r);
+ }
+
+ select_rc = pj_sock_select(FD_SETSIZE, &r, NULL, NULL, &timeout);
+ if (select_rc < 1)
+ continue;
+
+ for (i=0; i<sock_cnt; ++i) {
+ int sock_idx, srv_idx;
+ pj_ssize_t len;
+ pj_stun_msg msg;
+ pj_sockaddr_in addr;
+ int addrlen = sizeof(addr);
+ pj_stun_mapped_addr_attr *attr;
+ char recv_buf[128];
+
+ if (!PJ_FD_ISSET(sock[i], &r))
+ continue;
+
+ len = sizeof(recv_buf);
+ pj_sock_recvfrom( sock[i], recv_buf,
+ &len, 0,
+ (pj_sockaddr_t*)&addr,
+ &addrlen);
+
+ --wait_resp;
+
+ if (len < 1) {
+ mapped_status = PJ_STUN_ERR_TRANSPORT;
+ continue;
+ }
+
+ if (pj_stun_parse_msg(recv_buf, len, &msg) != 0) {
+ PJ_LOG(4,(THIS_FILE,
+ "Error parsing STUN response from %s:%d",
+ LOG_ADDR(addr)));
+ mapped_status = PJ_STUN_ERR_INVALID_MSG;
+ continue;
+ }
+
+ sock_idx = pj_ntohl(msg.hdr->tsx[2]);
+ srv_idx = pj_ntohl(msg.hdr->tsx[3]);
+
+ if (sock_idx<0 || sock_idx>=sock_cnt || srv_idx<0 || srv_idx>=2) {
+ PJ_LOG(4,(THIS_FILE,
+ "Invalid transaction ID from %s:%d",
+ LOG_ADDR(addr)));
+ mapped_status = PJ_STUN_ERR_INVALID_MSG;
+ continue;
+ }
+
+ if (pj_ntohs(msg.hdr->type) != PJ_STUN_BINDING_RESPONSE) {
+ PJ_LOG(4,(THIS_FILE,
+ "Non binding response %d from %s:%d",
+ pj_ntohs(msg.hdr->type), LOG_ADDR(addr)));
+ mapped_status = PJ_STUN_ERR_INVALID_MSG;
+ continue;
+ }
+
+ if (pj_stun_msg_find_attr(&msg, PJ_STUN_ATTR_ERROR_CODE) != NULL) {
+ PJ_LOG(4,(THIS_FILE,
+ "Got STUN error attribute from %s:%d",
+ LOG_ADDR(addr)));
+ mapped_status = PJ_STUN_ERR_INVALID_MSG;
+ continue;
+ }
+
+ attr = (void*)pj_stun_msg_find_attr(&msg, PJ_STUN_ATTR_MAPPED_ADDR);
+ if (!attr) {
+ PJ_LOG(4,(THIS_FILE,
+ "No mapped address in response from %s:%d",
+ LOG_ADDR(addr)));
+ mapped_status = PJ_STUN_ERR_INVALID_MSG;
+ continue;
+ }
+
+ rec[sock_idx].srv[srv_idx].mapped_addr = attr->addr;
+ rec[sock_idx].srv[srv_idx].mapped_port = attr->port;
+ }
+ }
+
+ /* The best scenario is if all requests have been replied.
+ * Then we don't need to go to the next retransmission iteration.
+ */
+ if (wait_resp <= 0)
+ break;
+ }
+
+ for (i=0; i<sock_cnt && mapped_status==0; ++i) {
+ if (rec[i].srv[0].mapped_addr == rec[i].srv[1].mapped_addr &&
+ rec[i].srv[0].mapped_port == rec[i].srv[1].mapped_port)
+ {
+ mapped_addr[i].sin_family = PJ_AF_INET;
+ mapped_addr[i].sin_addr.s_addr = rec[i].srv[0].mapped_addr;
+ mapped_addr[i].sin_port = (pj_uint16_t)rec[i].srv[0].mapped_port;
+
+ if (rec[i].srv[0].mapped_addr == 0 || rec[i].srv[0].mapped_port == 0) {
+ mapped_status = PJ_STUN_ERR_NO_RESPONSE;
+ }
+ } else {
+ mapped_status = PJ_STUN_ERR_SYMETRIC;
+ }
+ }
+
+ pj_pool_release(pool);
+
+ return mapped_status;
+
+on_error:
+ if (pool) pj_pool_release(pool);
+ return -1;
+}
+
+PJ_DEF(const char*) pj_stun_get_err_msg(pj_status_t status)
+{
+ switch (status) {
+ case 0: return "No error";
+ case -1: return "General error";
+ case PJ_STUN_ERR_MEMORY: return "Memory allocation failed";
+ case PJ_STUN_ERR_RESOLVE: return "Invalid IP or unable to resolve STUN server";
+ case PJ_STUN_ERR_TRANSPORT: return "Unable to contact STUN server";
+ case PJ_STUN_ERR_INVALID_MSG: return "Invalid response from STUN server";
+ case PJ_STUN_ERR_NO_RESPONSE: return "No response from STUN server";
+ case PJ_STUN_ERR_SYMETRIC: return "Different mappings are returned from servers";
+ }
+ return "Unknown error";
+}
diff --git a/pjlib/src/pj/symbols.c b/pjlib/src/pj/symbols.c
new file mode 100644
index 00000000..8c22ad8a
--- /dev/null
+++ b/pjlib/src/pj/symbols.c
@@ -0,0 +1,404 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/symbols.c 3 10/29/05 11:51a Bennylp $ */
+/*
+ * $Log: /pjproject-0.3/pjlib/src/pj/symbols.c $
+ *
+ * 3 10/29/05 11:51a Bennylp
+ * Version 0.3-pre2.
+ *
+ * 2 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 1 10/05/05 4:43p Bennylp
+ * Created.
+ *
+ */
+#include <pjlib.h>
+
+/*
+ * addr_resolv.h
+ */
+PJ_EXPORT_SYMBOL(pj_gethostbyname)
+
+/*
+ * array.h
+ */
+PJ_EXPORT_SYMBOL(pj_array_insert)
+PJ_EXPORT_SYMBOL(pj_array_erase)
+PJ_EXPORT_SYMBOL(pj_array_find)
+
+/*
+ * config.h
+ */
+PJ_EXPORT_SYMBOL(pj_dump_config)
+
+/*
+ * errno.h
+ */
+PJ_EXPORT_SYMBOL(pj_get_os_error)
+PJ_EXPORT_SYMBOL(pj_set_os_error)
+PJ_EXPORT_SYMBOL(pj_get_netos_error)
+PJ_EXPORT_SYMBOL(pj_set_netos_error)
+PJ_EXPORT_SYMBOL(pj_strerror)
+
+/*
+ * except.h
+ */
+PJ_EXPORT_SYMBOL(pj_throw_exception_)
+PJ_EXPORT_SYMBOL(pj_push_exception_handler_)
+PJ_EXPORT_SYMBOL(pj_pop_exception_handler_)
+PJ_EXPORT_SYMBOL(pj_setjmp)
+PJ_EXPORT_SYMBOL(pj_longjmp)
+PJ_EXPORT_SYMBOL(pj_exception_id_alloc)
+PJ_EXPORT_SYMBOL(pj_exception_id_free)
+PJ_EXPORT_SYMBOL(pj_exception_id_name)
+
+
+/*
+ * fifobuf.h
+ */
+PJ_EXPORT_SYMBOL(pj_fifobuf_init)
+PJ_EXPORT_SYMBOL(pj_fifobuf_max_size)
+PJ_EXPORT_SYMBOL(pj_fifobuf_alloc)
+PJ_EXPORT_SYMBOL(pj_fifobuf_unalloc)
+PJ_EXPORT_SYMBOL(pj_fifobuf_free)
+
+/*
+ * guid.h
+ */
+PJ_EXPORT_SYMBOL(pj_generate_unique_string)
+PJ_EXPORT_SYMBOL(pj_create_unique_string)
+
+/*
+ * hash.h
+ */
+PJ_EXPORT_SYMBOL(pj_hash_calc)
+PJ_EXPORT_SYMBOL(pj_hash_create)
+PJ_EXPORT_SYMBOL(pj_hash_get)
+PJ_EXPORT_SYMBOL(pj_hash_set)
+PJ_EXPORT_SYMBOL(pj_hash_count)
+PJ_EXPORT_SYMBOL(pj_hash_first)
+PJ_EXPORT_SYMBOL(pj_hash_next)
+PJ_EXPORT_SYMBOL(pj_hash_this)
+
+/*
+ * ioqueue.h
+ */
+PJ_EXPORT_SYMBOL(pj_ioqueue_create)
+PJ_EXPORT_SYMBOL(pj_ioqueue_destroy)
+PJ_EXPORT_SYMBOL(pj_ioqueue_set_lock)
+PJ_EXPORT_SYMBOL(pj_ioqueue_register_sock)
+PJ_EXPORT_SYMBOL(pj_ioqueue_unregister)
+PJ_EXPORT_SYMBOL(pj_ioqueue_get_user_data)
+PJ_EXPORT_SYMBOL(pj_ioqueue_poll)
+PJ_EXPORT_SYMBOL(pj_ioqueue_read)
+PJ_EXPORT_SYMBOL(pj_ioqueue_recv)
+PJ_EXPORT_SYMBOL(pj_ioqueue_recvfrom)
+PJ_EXPORT_SYMBOL(pj_ioqueue_write)
+PJ_EXPORT_SYMBOL(pj_ioqueue_send)
+PJ_EXPORT_SYMBOL(pj_ioqueue_sendto)
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
+PJ_EXPORT_SYMBOL(pj_ioqueue_accept)
+PJ_EXPORT_SYMBOL(pj_ioqueue_connect)
+#endif
+
+/*
+ * list.h
+ */
+PJ_EXPORT_SYMBOL(pj_list_insert_before)
+PJ_EXPORT_SYMBOL(pj_list_insert_nodes_before)
+PJ_EXPORT_SYMBOL(pj_list_insert_after)
+PJ_EXPORT_SYMBOL(pj_list_insert_nodes_after)
+PJ_EXPORT_SYMBOL(pj_list_merge_first)
+PJ_EXPORT_SYMBOL(pj_list_merge_last)
+PJ_EXPORT_SYMBOL(pj_list_erase)
+PJ_EXPORT_SYMBOL(pj_list_find_node)
+PJ_EXPORT_SYMBOL(pj_list_search)
+
+
+/*
+ * log.h
+ */
+PJ_EXPORT_SYMBOL(pj_log_write)
+#if PJ_LOG_MAX_LEVEL >= 1
+PJ_EXPORT_SYMBOL(pj_log_set_log_func)
+PJ_EXPORT_SYMBOL(pj_log_get_log_func)
+PJ_EXPORT_SYMBOL(pj_log_set_level)
+PJ_EXPORT_SYMBOL(pj_log_get_level)
+PJ_EXPORT_SYMBOL(pj_log_set_decor)
+PJ_EXPORT_SYMBOL(pj_log_get_decor)
+PJ_EXPORT_SYMBOL(pj_log_1)
+#endif
+#if PJ_LOG_MAX_LEVEL >= 2
+PJ_EXPORT_SYMBOL(pj_log_2)
+#endif
+#if PJ_LOG_MAX_LEVEL >= 3
+PJ_EXPORT_SYMBOL(pj_log_3)
+#endif
+#if PJ_LOG_MAX_LEVEL >= 4
+PJ_EXPORT_SYMBOL(pj_log_4)
+#endif
+#if PJ_LOG_MAX_LEVEL >= 5
+PJ_EXPORT_SYMBOL(pj_log_5)
+#endif
+#if PJ_LOG_MAX_LEVEL >= 6
+PJ_EXPORT_SYMBOL(pj_log_6)
+#endif
+
+/*
+ * md5.h
+ */
+PJ_EXPORT_SYMBOL(md5_init)
+PJ_EXPORT_SYMBOL(md5_append)
+PJ_EXPORT_SYMBOL(md5_finish)
+
+
+/*
+ * os.h
+ */
+PJ_EXPORT_SYMBOL(pj_init)
+PJ_EXPORT_SYMBOL(pj_getpid)
+PJ_EXPORT_SYMBOL(pj_thread_register)
+PJ_EXPORT_SYMBOL(pj_thread_create)
+PJ_EXPORT_SYMBOL(pj_thread_get_name)
+PJ_EXPORT_SYMBOL(pj_thread_resume)
+PJ_EXPORT_SYMBOL(pj_thread_this)
+PJ_EXPORT_SYMBOL(pj_thread_join)
+PJ_EXPORT_SYMBOL(pj_thread_destroy)
+PJ_EXPORT_SYMBOL(pj_thread_sleep)
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0
+PJ_EXPORT_SYMBOL(pj_thread_check_stack)
+PJ_EXPORT_SYMBOL(pj_thread_get_stack_max_usage)
+PJ_EXPORT_SYMBOL(pj_thread_get_stack_info)
+#endif
+PJ_EXPORT_SYMBOL(pj_atomic_create)
+PJ_EXPORT_SYMBOL(pj_atomic_destroy)
+PJ_EXPORT_SYMBOL(pj_atomic_set)
+PJ_EXPORT_SYMBOL(pj_atomic_get)
+PJ_EXPORT_SYMBOL(pj_atomic_inc)
+PJ_EXPORT_SYMBOL(pj_atomic_dec)
+PJ_EXPORT_SYMBOL(pj_thread_local_alloc)
+PJ_EXPORT_SYMBOL(pj_thread_local_free)
+PJ_EXPORT_SYMBOL(pj_thread_local_set)
+PJ_EXPORT_SYMBOL(pj_thread_local_get)
+PJ_EXPORT_SYMBOL(pj_enter_critical_section)
+PJ_EXPORT_SYMBOL(pj_leave_critical_section)
+PJ_EXPORT_SYMBOL(pj_mutex_create)
+PJ_EXPORT_SYMBOL(pj_mutex_lock)
+PJ_EXPORT_SYMBOL(pj_mutex_unlock)
+PJ_EXPORT_SYMBOL(pj_mutex_trylock)
+PJ_EXPORT_SYMBOL(pj_mutex_destroy)
+#if defined(PJ_DEBUG) && PJ_DEBUG != 0
+PJ_EXPORT_SYMBOL(pj_mutex_is_locked)
+#endif
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+PJ_EXPORT_SYMBOL(pj_sem_create)
+PJ_EXPORT_SYMBOL(pj_sem_wait)
+PJ_EXPORT_SYMBOL(pj_sem_trywait)
+PJ_EXPORT_SYMBOL(pj_sem_post)
+PJ_EXPORT_SYMBOL(pj_sem_destroy)
+#endif
+PJ_EXPORT_SYMBOL(pj_gettimeofday)
+PJ_EXPORT_SYMBOL(pj_time_decode)
+#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
+PJ_EXPORT_SYMBOL(pj_get_timestamp)
+PJ_EXPORT_SYMBOL(pj_get_timestamp_freq)
+PJ_EXPORT_SYMBOL(pj_elapsed_time)
+PJ_EXPORT_SYMBOL(pj_elapsed_usec)
+PJ_EXPORT_SYMBOL(pj_elapsed_nanosec)
+PJ_EXPORT_SYMBOL(pj_elapsed_cycle)
+#endif
+
+
+/*
+ * pool.h
+ */
+PJ_EXPORT_SYMBOL(pj_pool_create)
+PJ_EXPORT_SYMBOL(pj_pool_release)
+PJ_EXPORT_SYMBOL(pj_pool_getobjname)
+PJ_EXPORT_SYMBOL(pj_pool_reset)
+PJ_EXPORT_SYMBOL(pj_pool_get_capacity)
+PJ_EXPORT_SYMBOL(pj_pool_get_used_size)
+PJ_EXPORT_SYMBOL(pj_pool_alloc)
+PJ_EXPORT_SYMBOL(pj_pool_calloc)
+PJ_EXPORT_SYMBOL(pj_pool_factory_default_policy)
+PJ_EXPORT_SYMBOL(pj_pool_create_int)
+PJ_EXPORT_SYMBOL(pj_pool_init_int)
+PJ_EXPORT_SYMBOL(pj_pool_destroy_int)
+PJ_EXPORT_SYMBOL(pj_caching_pool_init)
+PJ_EXPORT_SYMBOL(pj_caching_pool_destroy)
+
+/*
+ * rand.h
+ */
+PJ_EXPORT_SYMBOL(pj_rand)
+PJ_EXPORT_SYMBOL(pj_srand)
+
+/*
+ * rbtree.h
+ */
+PJ_EXPORT_SYMBOL(pj_rbtree_init)
+PJ_EXPORT_SYMBOL(pj_rbtree_first)
+PJ_EXPORT_SYMBOL(pj_rbtree_last)
+PJ_EXPORT_SYMBOL(pj_rbtree_next)
+PJ_EXPORT_SYMBOL(pj_rbtree_prev)
+PJ_EXPORT_SYMBOL(pj_rbtree_insert)
+PJ_EXPORT_SYMBOL(pj_rbtree_find)
+PJ_EXPORT_SYMBOL(pj_rbtree_erase)
+PJ_EXPORT_SYMBOL(pj_rbtree_max_height)
+PJ_EXPORT_SYMBOL(pj_rbtree_min_height)
+
+/*
+ * scanner.h
+ */
+PJ_EXPORT_SYMBOL(pj_cs_init)
+PJ_EXPORT_SYMBOL(pj_cs_set)
+PJ_EXPORT_SYMBOL(pj_cs_add_range)
+PJ_EXPORT_SYMBOL(pj_cs_add_alpha)
+PJ_EXPORT_SYMBOL(pj_cs_add_num)
+PJ_EXPORT_SYMBOL(pj_cs_add_str)
+PJ_EXPORT_SYMBOL(pj_cs_del_range)
+PJ_EXPORT_SYMBOL(pj_cs_del_str)
+PJ_EXPORT_SYMBOL(pj_cs_invert)
+PJ_EXPORT_SYMBOL(pj_scan_init)
+PJ_EXPORT_SYMBOL(pj_scan_fini)
+PJ_EXPORT_SYMBOL(pj_scan_peek)
+PJ_EXPORT_SYMBOL(pj_scan_peek_n)
+PJ_EXPORT_SYMBOL(pj_scan_peek_until)
+PJ_EXPORT_SYMBOL(pj_scan_get)
+PJ_EXPORT_SYMBOL(pj_scan_get_quote)
+PJ_EXPORT_SYMBOL(pj_scan_get_n)
+PJ_EXPORT_SYMBOL(pj_scan_get_char)
+PJ_EXPORT_SYMBOL(pj_scan_get_newline)
+PJ_EXPORT_SYMBOL(pj_scan_get_until)
+PJ_EXPORT_SYMBOL(pj_scan_get_until_ch)
+PJ_EXPORT_SYMBOL(pj_scan_get_until_chr)
+PJ_EXPORT_SYMBOL(pj_scan_advance_n)
+PJ_EXPORT_SYMBOL(pj_scan_strcmp)
+PJ_EXPORT_SYMBOL(pj_scan_stricmp)
+PJ_EXPORT_SYMBOL(pj_scan_skip_whitespace)
+PJ_EXPORT_SYMBOL(pj_scan_save_state)
+PJ_EXPORT_SYMBOL(pj_scan_restore_state)
+
+/*
+ * sock.h
+ */
+PJ_EXPORT_SYMBOL(PJ_AF_UNIX)
+PJ_EXPORT_SYMBOL(PJ_AF_INET)
+PJ_EXPORT_SYMBOL(PJ_AF_INET6)
+PJ_EXPORT_SYMBOL(PJ_AF_PACKET)
+PJ_EXPORT_SYMBOL(PJ_AF_IRDA)
+PJ_EXPORT_SYMBOL(PJ_SOCK_STREAM)
+PJ_EXPORT_SYMBOL(PJ_SOCK_DGRAM)
+PJ_EXPORT_SYMBOL(PJ_SOCK_RAW)
+PJ_EXPORT_SYMBOL(PJ_SOCK_RDM)
+PJ_EXPORT_SYMBOL(PJ_SOL_SOCKET)
+PJ_EXPORT_SYMBOL(PJ_SOL_IP)
+PJ_EXPORT_SYMBOL(PJ_SOL_TCP)
+PJ_EXPORT_SYMBOL(PJ_SOL_UDP)
+PJ_EXPORT_SYMBOL(PJ_SOL_IPV6)
+PJ_EXPORT_SYMBOL(pj_ntohs)
+PJ_EXPORT_SYMBOL(pj_htons)
+PJ_EXPORT_SYMBOL(pj_ntohl)
+PJ_EXPORT_SYMBOL(pj_htonl)
+PJ_EXPORT_SYMBOL(pj_inet_ntoa)
+PJ_EXPORT_SYMBOL(pj_inet_aton)
+PJ_EXPORT_SYMBOL(pj_inet_addr)
+PJ_EXPORT_SYMBOL(pj_sockaddr_in_set_str_addr)
+PJ_EXPORT_SYMBOL(pj_sockaddr_in_init)
+PJ_EXPORT_SYMBOL(pj_gethostname)
+PJ_EXPORT_SYMBOL(pj_gethostaddr)
+PJ_EXPORT_SYMBOL(pj_sock_socket)
+PJ_EXPORT_SYMBOL(pj_sock_close)
+PJ_EXPORT_SYMBOL(pj_sock_bind)
+PJ_EXPORT_SYMBOL(pj_sock_bind_in)
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
+PJ_EXPORT_SYMBOL(pj_sock_listen)
+PJ_EXPORT_SYMBOL(pj_sock_accept)
+PJ_EXPORT_SYMBOL(pj_sock_shutdown)
+#endif
+PJ_EXPORT_SYMBOL(pj_sock_connect)
+PJ_EXPORT_SYMBOL(pj_sock_getpeername)
+PJ_EXPORT_SYMBOL(pj_sock_getsockname)
+PJ_EXPORT_SYMBOL(pj_sock_getsockopt)
+PJ_EXPORT_SYMBOL(pj_sock_setsockopt)
+PJ_EXPORT_SYMBOL(pj_sock_recv)
+PJ_EXPORT_SYMBOL(pj_sock_recvfrom)
+PJ_EXPORT_SYMBOL(pj_sock_send)
+PJ_EXPORT_SYMBOL(pj_sock_sendto)
+
+/*
+ * sock_select.h
+ */
+PJ_EXPORT_SYMBOL(PJ_FD_ZERO)
+PJ_EXPORT_SYMBOL(PJ_FD_SET)
+PJ_EXPORT_SYMBOL(PJ_FD_CLR)
+PJ_EXPORT_SYMBOL(PJ_FD_ISSET)
+PJ_EXPORT_SYMBOL(pj_sock_select)
+
+/*
+ * string.h
+ */
+PJ_EXPORT_SYMBOL(pj_str)
+PJ_EXPORT_SYMBOL(pj_strassign)
+PJ_EXPORT_SYMBOL(pj_strcpy)
+PJ_EXPORT_SYMBOL(pj_strcpy2)
+PJ_EXPORT_SYMBOL(pj_strdup)
+PJ_EXPORT_SYMBOL(pj_strdup_with_null)
+PJ_EXPORT_SYMBOL(pj_strdup2)
+PJ_EXPORT_SYMBOL(pj_strdup3)
+PJ_EXPORT_SYMBOL(pj_strcmp)
+PJ_EXPORT_SYMBOL(pj_strcmp2)
+PJ_EXPORT_SYMBOL(pj_strncmp)
+PJ_EXPORT_SYMBOL(pj_strncmp2)
+PJ_EXPORT_SYMBOL(pj_stricmp)
+PJ_EXPORT_SYMBOL(pj_stricmp2)
+PJ_EXPORT_SYMBOL(pj_strnicmp)
+PJ_EXPORT_SYMBOL(pj_strnicmp2)
+PJ_EXPORT_SYMBOL(pj_strcat)
+PJ_EXPORT_SYMBOL(pj_strltrim)
+PJ_EXPORT_SYMBOL(pj_strrtrim)
+PJ_EXPORT_SYMBOL(pj_strtrim)
+PJ_EXPORT_SYMBOL(pj_create_random_string)
+PJ_EXPORT_SYMBOL(pj_strtoul)
+PJ_EXPORT_SYMBOL(pj_utoa)
+PJ_EXPORT_SYMBOL(pj_utoa_pad)
+
+/*
+ * stun.h
+ */
+PJ_EXPORT_SYMBOL(pj_stun_create_bind_req)
+PJ_EXPORT_SYMBOL(pj_stun_parse_msg)
+PJ_EXPORT_SYMBOL(pj_stun_msg_find_attr)
+PJ_EXPORT_SYMBOL(pj_stun_get_mapped_addr)
+PJ_EXPORT_SYMBOL(pj_stun_get_err_msg)
+
+/*
+ * timer.h
+ */
+PJ_EXPORT_SYMBOL(pj_timer_heap_mem_size)
+PJ_EXPORT_SYMBOL(pj_timer_heap_create)
+PJ_EXPORT_SYMBOL(pj_timer_entry_init)
+PJ_EXPORT_SYMBOL(pj_timer_heap_schedule)
+PJ_EXPORT_SYMBOL(pj_timer_heap_cancel)
+PJ_EXPORT_SYMBOL(pj_timer_heap_count)
+PJ_EXPORT_SYMBOL(pj_timer_heap_earliest_time)
+PJ_EXPORT_SYMBOL(pj_timer_heap_poll)
+
+/*
+ * types.h
+ */
+PJ_EXPORT_SYMBOL(pj_time_val_normalize)
+
+/*
+ * xml.h
+ */
+PJ_EXPORT_SYMBOL(pj_xml_parse)
+PJ_EXPORT_SYMBOL(pj_xml_print)
+PJ_EXPORT_SYMBOL(pj_xml_add_node)
+PJ_EXPORT_SYMBOL(pj_xml_add_attr)
+PJ_EXPORT_SYMBOL(pj_xml_find_node)
+PJ_EXPORT_SYMBOL(pj_xml_find_next_node)
+PJ_EXPORT_SYMBOL(pj_xml_find_attr)
+PJ_EXPORT_SYMBOL(pj_xml_find)
+
diff --git a/pjlib/src/pj/timer.c b/pjlib/src/pj/timer.c
new file mode 100644
index 00000000..5cd09ea4
--- /dev/null
+++ b/pjlib/src/pj/timer.c
@@ -0,0 +1,504 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/timer.c 8 10/14/05 12:26a Bennylp $ */
+/* (C)1993-2003 Douglas C. Schmidt
+ *
+ * This file is originaly from ACE library by Doug Schmidt
+ * ACE(TM), TAO(TM) and CIAO(TM) are copyrighted by Douglas C. Schmidt and his research
+ * group at Washington University, University of California, Irvine, and Vanderbilt
+ * University Copyright (c) 1993-2003, all rights reserved.
+ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/timer.c $
+ *
+ * 8 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 7 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 6 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/timer.h>
+#include <pj/pool.h>
+#include <pj/os.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+
+#define HEAP_PARENT(X) (X == 0 ? 0 : (((X) - 1) / 2))
+#define HEAP_LEFT(X) (((X)+(X))+1)
+
+
+/**
+ * The implementation of timer heap.
+ */
+struct pj_timer_heap_t
+{
+ /** Pool from which the timer heap resize will get the storage from */
+ pj_pool_t *pool;
+
+ /** Maximum size of the heap. */
+ pj_size_t max_size;
+
+ /** Current size of the heap. */
+ pj_size_t cur_size;
+
+ /** Mutex for synchronization, or NULL */
+ pj_mutex_t *mutex;
+
+ /**
+ * Current contents of the Heap, which is organized as a "heap" of
+ * pj_timer_entry *'s. In this context, a heap is a "partially
+ * ordered, almost complete" binary tree, which is stored in an
+ * array.
+ */
+ pj_timer_entry **heap;
+
+ /**
+ * An array of "pointers" that allows each pj_timer_entry in the
+ * <heap_> to be located in O(1) time. Basically, <timer_id_[i]>
+ * contains the slot in the <heap_> array where an pj_timer_entry
+ * with timer id <i> resides. Thus, the timer id passed back from
+ * <schedule_entry> is really an slot into the <timer_ids> array. The
+ * <timer_ids_> array serves two purposes: negative values are
+ * treated as "pointers" for the <freelist_>, whereas positive
+ * values are treated as "pointers" into the <heap_> array.
+ */
+ pj_timer_id_t *timer_ids;
+
+ /**
+ * "Pointer" to the first element in the freelist contained within
+ * the <timer_ids_> array, which is organized as a stack.
+ */
+ pj_timer_id_t timer_ids_freelist;
+
+ /** Callback to be called when a timer expires. */
+ pj_timer_heap_callback *callback;
+
+};
+
+
+
+PJ_INLINE(void) lock_timer_heap( pj_timer_heap_t *ht )
+{
+ if (ht->mutex) {
+ pj_mutex_lock(ht->mutex);
+ }
+}
+
+PJ_INLINE(void) unlock_timer_heap( pj_timer_heap_t *ht )
+{
+ if (ht->mutex) {
+ pj_mutex_unlock(ht->mutex);
+ }
+}
+
+
+static void copy_node( pj_timer_heap_t *ht, int slot, pj_timer_entry *moved_node )
+{
+ PJ_CHECK_STACK();
+
+ // Insert <moved_node> into its new location in the heap.
+ ht->heap[slot] = moved_node;
+
+ // Update the corresponding slot in the parallel <timer_ids_> array.
+ ht->timer_ids[moved_node->_timer_id] = slot;
+}
+
+static pj_timer_id_t pop_freelist( pj_timer_heap_t *ht )
+{
+ // We need to truncate this to <int> for backwards compatibility.
+ pj_timer_id_t new_id = ht->timer_ids_freelist;
+
+ PJ_CHECK_STACK();
+
+ // The freelist values in the <timer_ids_> are negative, so we need
+ // to negate them to get the next freelist "pointer."
+ ht->timer_ids_freelist =
+ -ht->timer_ids[ht->timer_ids_freelist];
+
+ return new_id;
+
+}
+
+static void push_freelist (pj_timer_heap_t *ht, pj_timer_id_t old_id)
+{
+ PJ_CHECK_STACK();
+
+ // The freelist values in the <timer_ids_> are negative, so we need
+ // to negate them to get the next freelist "pointer."
+ ht->timer_ids[old_id] = -ht->timer_ids_freelist;
+ ht->timer_ids_freelist = old_id;
+}
+
+
+static void reheap_down(pj_timer_heap_t *ht, pj_timer_entry *moved_node,
+ size_t slot, size_t child)
+{
+ PJ_CHECK_STACK();
+
+ // Restore the heap property after a deletion.
+
+ while (child < ht->cur_size)
+ {
+ // Choose the smaller of the two children.
+ if (child + 1 < ht->cur_size
+ && PJ_TIME_VAL_LT(ht->heap[child + 1]->_timer_value, ht->heap[child]->_timer_value))
+ child++;
+
+ // Perform a <copy> if the child has a larger timeout value than
+ // the <moved_node>.
+ if (PJ_TIME_VAL_LT(ht->heap[child]->_timer_value, moved_node->_timer_value))
+ {
+ copy_node( ht, slot, ht->heap[child]);
+ slot = child;
+ child = HEAP_LEFT(child);
+ }
+ else
+ // We've found our location in the heap.
+ break;
+ }
+
+ copy_node( ht, slot, moved_node);
+}
+
+static void reheap_up( pj_timer_heap_t *ht, pj_timer_entry *moved_node,
+ size_t slot, size_t parent)
+{
+ // Restore the heap property after an insertion.
+
+ while (slot > 0)
+ {
+ // If the parent node is greater than the <moved_node> we need
+ // to copy it down.
+ if (PJ_TIME_VAL_LT(moved_node->_timer_value, ht->heap[parent]->_timer_value))
+ {
+ copy_node(ht, slot, ht->heap[parent]);
+ slot = parent;
+ parent = HEAP_PARENT(slot);
+ }
+ else
+ break;
+ }
+
+ // Insert the new node into its proper resting place in the heap and
+ // update the corresponding slot in the parallel <timer_ids> array.
+ copy_node(ht, slot, moved_node);
+}
+
+
+static pj_timer_entry * remove_node( pj_timer_heap_t *ht, size_t slot)
+{
+ pj_timer_entry *removed_node = ht->heap[slot];
+
+ // Return this timer id to the freelist.
+ push_freelist( ht, removed_node->_timer_id );
+
+ // Decrement the size of the heap by one since we're removing the
+ // "slot"th node.
+ ht->cur_size--;
+
+ // Set the ID
+ removed_node->_timer_id = -1;
+
+ // Only try to reheapify if we're not deleting the last entry.
+
+ if (slot < ht->cur_size)
+ {
+ int parent;
+ pj_timer_entry *moved_node = ht->heap[ht->cur_size];
+
+ // Move the end node to the location being removed and update
+ // the corresponding slot in the parallel <timer_ids> array.
+ copy_node( ht, slot, moved_node);
+
+ // If the <moved_node->time_value_> is great than or equal its
+ // parent it needs be moved down the heap.
+ parent = HEAP_PARENT (slot);
+
+ if (PJ_TIME_VAL_GTE(moved_node->_timer_value, ht->heap[parent]->_timer_value))
+ reheap_down( ht, moved_node, slot, HEAP_LEFT(slot));
+ else
+ reheap_up( ht, moved_node, slot, parent);
+ }
+
+ return removed_node;
+}
+
+static void grow_heap(pj_timer_heap_t *ht)
+{
+ // All the containers will double in size from max_size_
+ size_t new_size = ht->max_size * 2;
+ pj_timer_id_t *new_timer_ids;
+ pj_size_t i;
+
+ // First grow the heap itself.
+
+ pj_timer_entry **new_heap = 0;
+
+ new_heap = pj_pool_alloc(ht->pool, sizeof(pj_timer_entry*) * new_size);
+ memcpy(new_heap, ht->heap, ht->max_size * sizeof(pj_timer_entry*));
+ //delete [] this->heap_;
+ ht->heap = new_heap;
+
+ // Grow the array of timer ids.
+
+ new_timer_ids = 0;
+ new_timer_ids = pj_pool_alloc(ht->pool, new_size * sizeof(pj_timer_id_t));
+
+ memcpy( new_timer_ids, ht->timer_ids, ht->max_size * sizeof(pj_timer_id_t));
+
+ //delete [] timer_ids_;
+ ht->timer_ids = new_timer_ids;
+
+ // And add the new elements to the end of the "freelist".
+ for (i = ht->max_size; i < new_size; i++)
+ ht->timer_ids[i] = -((pj_timer_id_t) (i + 1));
+
+ ht->max_size = new_size;
+}
+
+static void insert_node(pj_timer_heap_t *ht, pj_timer_entry *new_node)
+{
+ if (ht->cur_size + 2 >= ht->max_size)
+ grow_heap(ht);
+
+ reheap_up( ht, new_node, ht->cur_size, HEAP_PARENT(ht->cur_size));
+ ht->cur_size++;
+}
+
+
+static pj_status_t schedule_entry( pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ const pj_time_val *future_time )
+{
+ if (ht->cur_size < ht->max_size)
+ {
+ // Obtain the next unique sequence number.
+ // Set the entry
+ entry->_timer_id = pop_freelist(ht);
+ entry->_timer_value = *future_time;
+ insert_node( ht, entry);
+ return 0;
+ }
+ else
+ return -1;
+}
+
+
+static int cancel( pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ int dont_call)
+{
+ long timer_node_slot;
+
+ PJ_CHECK_STACK();
+
+ // Check to see if the timer_id is out of range
+ if (entry->_timer_id < 0 || (pj_size_t)entry->_timer_id > ht->max_size)
+ return 0;
+
+ timer_node_slot = ht->timer_ids[entry->_timer_id];
+
+ if (timer_node_slot < 0) // Check to see if timer_id is still valid.
+ return 0;
+
+ if (entry != ht->heap[timer_node_slot])
+ {
+ pj_assert(entry == ht->heap[timer_node_slot]);
+ return 0;
+ }
+ else
+ {
+ remove_node( ht, timer_node_slot);
+
+ if (dont_call == 0)
+ // Call the close hook.
+ (*ht->callback)(ht, entry);
+ return 1;
+ }
+}
+
+
+/*
+ * Calculate memory size required to create a timer heap.
+ */
+PJ_DEF(pj_size_t) pj_timer_heap_mem_size(pj_size_t count)
+{
+ return /* size of the timer heap itself: */
+ sizeof(pj_timer_heap_t) +
+ /* size of each entry: */
+ (count+2) * (sizeof(pj_timer_entry*)+sizeof(pj_timer_id_t)) +
+ /* mutex, pool etc: */
+ 132;
+}
+
+/*
+ * Create a new timer heap.
+ */
+PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool,
+ pj_size_t size,
+ unsigned flag,
+ pj_timer_heap_t **p_heap)
+{
+ pj_timer_heap_t *ht;
+ pj_size_t i;
+
+ PJ_ASSERT_RETURN(pool && p_heap, PJ_EINVAL);
+
+ *p_heap = NULL;
+
+ /* Magic? */
+ size += 2;
+
+ /* Allocate timer heap data structure from the pool */
+ ht = pj_pool_alloc(pool, sizeof(pj_timer_heap_t));
+ if (!ht)
+ return PJ_ENOMEM;
+
+ /* Initialize timer heap sizes */
+ ht->max_size = size;
+ ht->cur_size = 0;
+ ht->timer_ids_freelist = 1;
+ ht->pool = pool;
+
+ /* Mutex. */
+ if (flag & PJ_TIMER_HEAP_NO_SYNCHRONIZE) {
+ ht->mutex = NULL;
+ } else {
+ pj_status_t rc;
+
+ /* Mutex must be the recursive types.
+ * See commented code inside pj_timer_heap_poll()
+ */
+ rc = pj_mutex_create(pool, "tmhp%p", PJ_MUTEX_RECURSE, &ht->mutex);
+ if (rc != PJ_SUCCESS)
+ return rc;
+ }
+
+ // Create the heap array.
+ ht->heap = pj_pool_alloc(pool, sizeof(pj_timer_entry*) * size);
+ if (!ht->heap)
+ return PJ_ENOMEM;
+
+ // Create the parallel
+ ht->timer_ids = pj_pool_alloc( pool, sizeof(pj_timer_id_t) * size);
+ if (!ht->timer_ids)
+ return PJ_ENOMEM;
+
+ // Initialize the "freelist," which uses negative values to
+ // distinguish freelist elements from "pointers" into the <heap_>
+ // array.
+ for (i=0; i<size; ++i)
+ ht->timer_ids[i] = -((pj_timer_id_t) (i + 1));
+
+ *p_heap = ht;
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
+ int id,
+ void *user_data,
+ pj_timer_heap_callback *cb )
+{
+ pj_assert(entry && cb);
+
+ entry->id = id;
+ entry->user_data = user_data;
+ entry->cb = cb;
+
+ return entry;
+}
+
+PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ const pj_time_val *delay)
+{
+ pj_status_t status;
+ pj_time_val expires;
+
+ PJ_ASSERT_RETURN(ht && entry && delay, PJ_EINVAL);
+
+ pj_gettimeofday(&expires);
+ PJ_TIME_VAL_ADD(expires, *delay);
+
+ lock_timer_heap(ht);
+ status = schedule_entry(ht, entry, &expires);
+ unlock_timer_heap(ht);
+
+ return status;
+}
+
+PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
+ pj_timer_entry *entry)
+{
+ int count;
+
+ PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL);
+
+ lock_timer_heap(ht);
+ count = cancel(ht, entry, 1);
+ unlock_timer_heap(ht);
+
+ return count;
+}
+
+PJ_DEF(int) pj_timer_heap_poll( pj_timer_heap_t *ht, pj_time_val *next_delay )
+{
+ pj_time_val now;
+ int count;
+
+ PJ_ASSERT_RETURN(ht, -1);
+
+ if (!ht->cur_size && next_delay) {
+ next_delay->sec = next_delay->msec = PJ_MAXINT32;
+ return 0;
+ }
+
+ count = 0;
+ pj_gettimeofday(&now);
+
+ lock_timer_heap(ht);
+ while ( ht->cur_size &&
+ PJ_TIME_VAL_LTE(ht->heap[0]->_timer_value, now) )
+ {
+ pj_timer_entry *node = remove_node(ht, 0);
+ ++count;
+
+ //Better not to temporarily release mutex to save some syscalls.
+ //But then make sure the mutex must be the recursive types (PJ_MUTEX_RECURSE)!
+ //unlock_timer_heap(ht);
+ (*node->cb)(ht, node);
+ //lock_timer_heap(ht);
+ }
+ if (ht->cur_size && next_delay) {
+ *next_delay = ht->heap[0]->_timer_value;
+ PJ_TIME_VAL_SUB(*next_delay, now);
+ } else if (next_delay) {
+ next_delay->sec = next_delay->msec = PJ_MAXINT32;
+ }
+ unlock_timer_heap(ht);
+
+ return count;
+}
+
+PJ_DEF(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht )
+{
+ return ht->cur_size;
+}
+
+PJ_DEF(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t * ht,
+ pj_time_val *timeval)
+{
+ pj_assert(ht->cur_size != 0);
+ if (ht->cur_size == 0)
+ return PJ_ENOTFOUND;
+
+ lock_timer_heap(ht);
+ *timeval = ht->heap[0]->_timer_value;
+ unlock_timer_heap(ht);
+
+ return PJ_SUCCESS;
+}
+
diff --git a/pjlib/src/pj/tounix b/pjlib/src/pj/tounix
new file mode 100644
index 00000000..f4e1920b
--- /dev/null
+++ b/pjlib/src/pj/tounix
@@ -0,0 +1,4 @@
+#!/bin/sh
+cp $1 /tmp
+cat /tmp/$1 | tr -d '\r' > $1
+
diff --git a/pjlib/src/pj/types.c b/pjlib/src/pj/types.c
new file mode 100644
index 00000000..ee1a8588
--- /dev/null
+++ b/pjlib/src/pj/types.c
@@ -0,0 +1,36 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/types.c 4 9/17/05 10:37a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/types.c $
+ *
+ * 4 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/types.h>
+#include <pj/os.h>
+
+void pj_time_val_normalize(pj_time_val *t)
+{
+ PJ_CHECK_STACK();
+
+ if (t->msec >= 1000) {
+ do {
+ t->sec++;
+ t->msec -= 1000;
+ } while (t->msec >= 1000);
+ }
+ else if (t->msec <= -1000) {
+ do {
+ t->sec--;
+ t->msec += 1000;
+ } while (t->msec <= -1000);
+ }
+
+ if (t->sec >= 1 && t->msec < 0) {
+ t->sec--;
+ t->msec += 1000;
+
+ } else if (t->sec < 0 && t->msec > 0) {
+ t->sec++;
+ t->msec -= 1000;
+ }
+}
diff --git a/pjlib/src/pj/xml.c b/pjlib/src/pj/xml.c
new file mode 100644
index 00000000..ff3684a8
--- /dev/null
+++ b/pjlib/src/pj/xml.c
@@ -0,0 +1,392 @@
+/* $Header: /pjproject-0.3/pjlib/src/pj/xml.c 9 10/14/05 12:26a Bennylp $ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/xml.c $
+ *
+ * 9 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 8 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 7 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#include <pj/xml.h>
+#include <pj/scanner.h>
+#include <pj/except.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/log.h>
+#include <pj/os.h>
+
+#define EX_SYNTAX_ERROR 12
+#define THIS_FILE "xml.c"
+
+static void on_syntax_error(struct pj_scanner *scanner)
+{
+ PJ_UNUSED_ARG(scanner);
+ PJ_THROW(EX_SYNTAX_ERROR);
+}
+
+static pj_xml_node *alloc_node( pj_pool_t *pool )
+{
+ pj_xml_node *node;
+
+ node = pj_pool_calloc(pool, 1, sizeof(pj_xml_node));
+ pj_list_init( &node->attr_head );
+ pj_list_init( &node->node_head );
+
+ return node;
+}
+
+static pj_xml_attr *alloc_attr( pj_pool_t *pool )
+{
+ return pj_pool_calloc(pool, 1, sizeof(pj_xml_attr));
+}
+
+/* This is a recursive function! */
+static pj_xml_node *xml_parse_node( pj_pool_t *pool, pj_scanner *scanner)
+{
+ pj_xml_node *node;
+ pj_str_t end_name;
+
+ PJ_CHECK_STACK();
+
+ if (*scanner->curptr != '<')
+ on_syntax_error(scanner);
+
+ /* Handle Processing Instructino (PI) construct (i.e. "<?") */
+ if (*scanner->curptr == '<' && *(scanner->curptr+1) == '?') {
+ pj_scan_advance_n(scanner, 2, PJ_FALSE);
+ for (;;) {
+ pj_str_t dummy;
+ pj_scan_get_until_ch(scanner, '?', &dummy);
+ if (*scanner->curptr=='?' && *(scanner->curptr+1)=='>') {
+ pj_scan_advance_n(scanner, 2, PJ_TRUE);
+ break;
+ } else {
+ pj_scan_advance_n(scanner, 1, PJ_FALSE);
+ }
+ }
+ return xml_parse_node(pool, scanner);
+ }
+
+ /* Handle comments construct (i.e. "<!--") */
+ if (pj_scan_strcmp(scanner, "<!--", 4) == 0) {
+ pj_scan_advance_n(scanner, 4, PJ_FALSE);
+ for (;;) {
+ pj_str_t dummy;
+ pj_scan_get_until_ch(scanner, '-', &dummy);
+ if (pj_scan_strcmp(scanner, "-->", 3) == 0) {
+ pj_scan_advance_n(scanner, 3, PJ_TRUE);
+ break;
+ } else {
+ pj_scan_advance_n(scanner, 1, PJ_FALSE);
+ }
+ }
+ return xml_parse_node(pool, scanner);
+ }
+
+ /* Alloc node. */
+ node = alloc_node(pool);
+
+ /* Get '<' */
+ pj_scan_get_char(scanner);
+
+ /* Get node name. */
+ pj_scan_get_until_chr( scanner, " />\t", &node->name);
+
+ /* Get attributes. */
+ while (*scanner->curptr != '>' && *scanner->curptr != '/') {
+ pj_xml_attr *attr = alloc_attr(pool);
+
+ pj_scan_get_until_chr( scanner, "=> \t", &attr->name);
+ if (*scanner->curptr == '=') {
+ pj_scan_get_char( scanner );
+ pj_scan_get_quote(scanner, '"', '"', &attr->value);
+ /* remove quote characters */
+ ++attr->value.ptr;
+ attr->value.slen -= 2;
+ }
+
+ pj_list_insert_before( &node->attr_head, attr );
+ }
+
+ if (*scanner->curptr == '/') {
+ pj_scan_get_char(scanner);
+ if (pj_scan_get_char(scanner) != '>')
+ on_syntax_error(scanner);
+ return node;
+ }
+
+ /* Enclosing bracket. */
+ if (pj_scan_get_char(scanner) != '>')
+ on_syntax_error(scanner);
+
+ /* Sub nodes. */
+ while (*scanner->curptr == '<' && *(scanner->curptr+1) != '/') {
+ pj_xml_node *sub_node = xml_parse_node(pool, scanner);
+ pj_list_insert_before( &node->node_head, sub_node );
+ }
+
+ /* Content. */
+ if (!pj_scan_is_eof(scanner) && *scanner->curptr != '<') {
+ pj_scan_get_until_ch(scanner, '<', &node->content);
+ }
+
+ /* Enclosing node. */
+ if (pj_scan_get_char(scanner) != '<' || pj_scan_get_char(scanner) != '/')
+ on_syntax_error(scanner);
+
+ pj_scan_get_until_chr(scanner, " \t>", &end_name);
+
+ /* Compare name. */
+ if (pj_stricmp(&node->name, &end_name) != 0)
+ on_syntax_error(scanner);
+
+ /* Enclosing '>' */
+ if (pj_scan_get_char(scanner) != '>')
+ on_syntax_error(scanner);
+
+ return node;
+}
+
+PJ_DEF(pj_xml_node*) pj_xml_parse( pj_pool_t *pool, char *msg, pj_size_t len)
+{
+ pj_xml_node *node = NULL;
+ pj_scanner scanner;
+ PJ_USE_EXCEPTION;
+
+ if (!msg || !len || !pool)
+ return NULL;
+
+ pj_scan_init( &scanner, msg, len,
+ PJ_SCAN_AUTOSKIP_WS|PJ_SCAN_AUTOSKIP_NEWLINE,
+ &on_syntax_error);
+ PJ_TRY {
+ node = xml_parse_node(pool, &scanner);
+ }
+ PJ_DEFAULT {
+ PJ_LOG(4,(THIS_FILE, "Syntax error parsing XML in line %d column %d",
+ scanner.line, scanner.col));
+ }
+ PJ_END;
+ pj_scan_fini( &scanner );
+ return node;
+}
+
+/* This is a recursive function. */
+static int xml_print_node( const pj_xml_node *node, int indent,
+ char *buf, pj_size_t len )
+{
+ int i;
+ char *p = buf;
+ pj_xml_attr *attr;
+ pj_xml_node *sub_node;
+
+#define SIZE_LEFT() ((int)(len - (p-buf)))
+
+ PJ_CHECK_STACK();
+
+ /* Print name. */
+ if (SIZE_LEFT() < node->name.slen + indent + 5)
+ return -1;
+ for (i=0; i<indent; ++i)
+ *p++ = ' ';
+ *p++ = '<';
+ pj_memcpy(p, node->name.ptr, node->name.slen);
+ p += node->name.slen;
+
+ /* Print attributes. */
+ attr = node->attr_head.next;
+ while (attr != &node->attr_head) {
+
+ if (SIZE_LEFT() < attr->name.slen + attr->value.slen + 4)
+ return -1;
+
+ *p++ = ' ';
+
+ /* Attribute name. */
+ pj_memcpy(p, attr->name.ptr, attr->name.slen);
+ p += attr->name.slen;
+
+ /* Attribute value. */
+ if (attr->value.slen) {
+ *p++ = '=';
+ *p++ = '"';
+ pj_memcpy(p, attr->value.ptr, attr->value.slen);
+ p += attr->value.slen;
+ *p++ = '"';
+ }
+
+ attr = attr->next;
+ }
+
+ /* Check for empty node. */
+ if (node->content.slen==0 &&
+ node->node_head.next==(pj_xml_node*)&node->node_head)
+ {
+ *p++ = ' ';
+ *p++ = '/';
+ *p++ = '>';
+ return p-buf;
+ }
+
+ /* Enclosing '>' */
+ if (SIZE_LEFT() < 1) return -1;
+ *p++ = '>';
+
+ /* Print sub nodes. */
+ sub_node = node->node_head.next;
+ while (sub_node != (pj_xml_node*)&node->node_head) {
+ int printed;
+
+ if (SIZE_LEFT() < indent + 3)
+ return -1;
+ //*p++ = '\r';
+ *p++ = '\n';
+
+ printed = xml_print_node(sub_node, indent + 1, p, SIZE_LEFT());
+ if (printed < 0)
+ return -1;
+
+ p += printed;
+ sub_node = sub_node->next;
+ }
+
+ /* Content. */
+ if (node->content.slen) {
+ if (SIZE_LEFT() < node->content.slen) return -1;
+ pj_memcpy(p, node->content.ptr, node->content.slen);
+ p += node->content.slen;
+ }
+
+ /* Enclosing node. */
+ if (node->node_head.next != (pj_xml_node*)&node->node_head) {
+ if (SIZE_LEFT() < node->name.slen + 5 + indent)
+ return -1;
+ //*p++ = '\r';
+ *p++ = '\n';
+ for (i=0; i<indent; ++i)
+ *p++ = ' ';
+ } else {
+ if (SIZE_LEFT() < node->name.slen + 3)
+ return -1;
+ }
+ *p++ = '<';
+ *p++ = '/';
+ pj_memcpy(p, node->name.ptr, node->name.slen);
+ p += node->name.slen;
+ *p++ = '>';
+
+#undef SIZE_LEFT
+
+ return p - buf;
+}
+
+PJ_DEF(int) pj_xml_print(const pj_xml_node *node, char *buf, pj_size_t len,
+ pj_bool_t include_prolog)
+{
+ int prolog_len = 0;
+ int printed;
+
+ if (!node || !buf || !len)
+ return 0;
+
+ if (include_prolog) {
+ pj_str_t prolog = {"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", 39};
+ if ((int)len < prolog.slen)
+ return -1;
+ pj_memcpy(buf, prolog.ptr, prolog.slen);
+ prolog_len = prolog.slen;
+ }
+
+ printed = xml_print_node(node, 0, buf+prolog_len, len-prolog_len) + prolog_len;
+ if (printed > 0 && len-printed >= 1) {
+ buf[printed++] = '\n';
+ }
+ return printed;
+}
+
+
+PJ_DEF(void) pj_xml_add_node( pj_xml_node *parent, pj_xml_node *node )
+{
+ pj_list_insert_before(&parent->node_head, node);
+}
+
+PJ_DEF(void) pj_xml_add_attr( pj_xml_node *node, pj_xml_attr *attr )
+{
+ pj_list_insert_before(&node->attr_head, attr);
+}
+
+PJ_DEF(pj_xml_node*) pj_xml_find_node(pj_xml_node *parent, const pj_str_t *name)
+{
+ pj_xml_node *node = parent->node_head.next;
+
+ PJ_CHECK_STACK();
+
+ while (node != (void*)&parent->node_head) {
+ if (pj_stricmp(&node->name, name) == 0)
+ return node;
+ node = node->next;
+ }
+ return NULL;
+}
+
+
+PJ_DEF(pj_xml_node*) pj_xml_find_next_node( pj_xml_node *parent, pj_xml_node *node,
+ const pj_str_t *name)
+{
+ PJ_CHECK_STACK();
+
+ node = node->next;
+ while (node != (void*)&parent->node_head) {
+ if (pj_stricmp(&node->name, name) == 0)
+ return node;
+ node = node->next;
+ }
+ return NULL;
+}
+
+
+PJ_DEF(pj_xml_attr*) pj_xml_find_attr( pj_xml_node *node, const pj_str_t *name,
+ const pj_str_t *value)
+{
+ pj_xml_attr *attr = node->attr_head.next;
+ while (attr != (void*)&node->attr_head) {
+ if (pj_stricmp(&attr->name, name)==0) {
+ if (value) {
+ if (pj_stricmp(&attr->value, value)==0)
+ return attr;
+ } else {
+ return attr;
+ }
+ }
+ attr = attr->next;
+ }
+ return NULL;
+}
+
+
+
+PJ_DEF(pj_xml_node*) pj_xml_find( pj_xml_node *parent, const pj_str_t *name,
+ const void *data,
+ pj_bool_t (*match)(pj_xml_node *, const void*))
+{
+ pj_xml_node *head = (void*)&parent->node_head, *node = head->next;
+
+ while (node != (void*)head) {
+ if (name && pj_stricmp(&node->name, name)==0) {
+ if (match) {
+ if (match(node, data))
+ return node;
+ } else {
+ return node;
+ }
+ }
+ node = node->next;
+ }
+ return NULL;
+}
+