Index: pjproject/main/pjlib/build/os-win32.mak
===================================================================
--- pjproject/main/pjlib/build/os-win32.mak	(revision 17)
+++ pjproject/main/pjlib/build/os-win32.mak	(revision 18)
@@ -8,9 +8,15 @@
 # to all operating systems should go in Makefile instead.
 #
-export PJLIB_OBJS += 	addr_resolv_sock.o guid_win32.o ioqueue_winnt.o \
+export PJLIB_OBJS += 	addr_resolv_sock.o guid_win32.o  \
 			log_writer_stdout.o os_core_win32.o \
 			os_error_win32.o os_time_ansi.o os_timestamp_common.o \
 			os_timestamp_win32.o \
 			pool_policy_malloc.o sock_bsd.o sock_select.o
+
+export PJLIB_OBJS +=	ioqueue_win32.o
+#export PJLIB_OBJS +=	ioqueue_select.o
+
+export PJLIB_OBJS +=	file_io_win32.o
+#export PJLIB_OBJS +=	file_io_ansi.o
 
 #
Index: pjproject/main/pjlib/build/pjlib.dsp
===================================================================
--- pjproject/main/pjlib/build/pjlib.dsp	(revision 17)
+++ pjproject/main/pjlib/build/pjlib.dsp	(revision 18)
@@ -195,4 +195,17 @@
 # Begin Source File
 
+SOURCE=..\src\pj\file_access_win32.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\file_io_ansi.c
+# PROP Exclude_From_Build 1
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pj\file_io_win32.c
+# End Source File
+# Begin Source File
+
 SOURCE=..\src\pj\guid.c
 # End Source File
@@ -474,4 +487,12 @@
 
 SOURCE=..\include\pj\fifobuf.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\pj\file_access.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\pj\file_io.h
 # End Source File
 # Begin Source File
Index: pjproject/main/pjlib/build/pjlib_test.dsp
===================================================================
--- pjproject/main/pjlib/build/pjlib_test.dsp	(revision 17)
+++ pjproject/main/pjlib/build/pjlib_test.dsp	(revision 18)
@@ -108,4 +108,8 @@
 # Begin Source File
 
+SOURCE="..\src\pjlib-test\file.c"
+# End Source File
+# Begin Source File
+
 SOURCE="..\src\pjlib-test\ioq_perf.c"
 # End Source File
Index: pjproject/main/pjlib/include/pj/addr_resolv.h
===================================================================
--- pjproject/main/pjlib/include/pj/addr_resolv.h	(revision 17)
+++ pjproject/main/pjlib/include/pj/addr_resolv.h	(revision 18)
@@ -15,5 +15,5 @@
 
 /**
- * @defgroup pj_addr_resolve Address Resolution
+ * @defgroup pj_addr_resolve Network Address Resolution
  * @ingroup PJ_IO
  * @{
Index: pjproject/main/pjlib/include/pj/file_access.h
===================================================================
--- pjproject/main/pjlib/include/pj/file_access.h	(revision 18)
+++ pjproject/main/pjlib/include/pj/file_access.h	(revision 18)
@@ -0,0 +1,91 @@
+/* $Id */
+#ifndef __PJ_FILE_ACCESS_H__
+#define __PJ_FILE_ACCESS_H__
+
+/**
+ * @file file_access.h
+ * @brief File manipulation and access.
+ */
+#include <pj/types.h>
+
+PJ_BEGIN_DECL 
+
+/**
+ * @defgroup PJ_FILE_ACCESS File Access
+ * @ingroup PJ_IO
+ * @{
+ *
+ */
+
+/**
+ * This structure describes file information, to be obtained by
+ * calling #pj_file_getstat(). The time information in this structure
+ * is in local time.
+ */
+typedef struct pj_file_stat
+{
+    pj_off_t        size;   /**< Total file size.               */
+    pj_time_val     atime;  /**< Time of last access.           */
+    pj_time_val     mtime;  /**< Time of last modification.     */
+    pj_time_val     ctime;  /**< Time of last creation.         */
+} pj_file_stat;
+
+
+/**
+ * Returns non-zero if the specified file exists.
+ *
+ * @param filename      The file name.
+ *
+ * @return              Non-zero if the file exists.
+ */
+PJ_DECL(pj_bool_t) pj_file_exists(const char *filename);
+
+/**
+ * Returns the size of the file.
+ *
+ * @param filename      The file name.
+ *
+ * @return              The file size in bytes or -1 on error.
+ */
+PJ_DECL(pj_off_t) pj_file_size(const char *filename);
+
+/**
+ * Delete a file.
+ *
+ * @param filename      The filename.
+ *
+ * @return              PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_file_delete(const char *filename);
+
+/**
+ * Move a \c oldname to \c newname. If \c newname already exists,
+ * it will be overwritten.
+ *
+ * @param oldname       The file to rename.
+ * @param newname       New filename to assign.
+ *
+ * @return              PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_file_move( const char *oldname, 
+                                   const char *newname);
+
+
+/**
+ * Return information about the specified file. The time information in
+ * the \c stat structure will be in local time.
+ *
+ * @param filename      The filename.
+ * @param stat          Pointer to variable to receive file information.
+ *
+ * @return              PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_file_getstat(const char *filename, pj_file_stat *stat);
+
+
+/** @} */
+
+PJ_END_DECL
+
+
+#endif	/* __PJ_FILE_ACCESS_H__ */
Index: pjproject/main/pjlib/include/pj/file_io.h
===================================================================
--- pjproject/main/pjlib/include/pj/file_io.h	(revision 18)
+++ pjproject/main/pjlib/include/pj/file_io.h	(revision 18)
@@ -0,0 +1,155 @@
+/* $Id$ */
+#ifndef __PJ_FILE_IO_H__
+#define __PJ_FILE_IO_H__
+
+/**
+ * @file file_io.h
+ * @brief Simple file I/O abstraction.
+ */
+#include <pj/types.h>
+
+PJ_BEGIN_DECL 
+
+/**
+ * @defgroup PJ_FILE_IO File I/O
+ * @ingroup PJ_IO
+ * @{
+ *
+ * This file contains functionalities to perform file I/O. The file
+ * I/O can be implemented with various back-end, either using native
+ * file API or ANSI stream. 
+ *
+ * @section pj_file_size_limit_sec Size Limits
+ *
+ * There may be limitation on the size that can be handled by the
+ * #pj_file_setpos() or #pj_file_getpos() functions. The API itself
+ * uses 64-bit integer for the file offset/position (where available); 
+ * however some backends (such as ANSI) may only support signed 32-bit 
+ * offset resolution.
+ *
+ * Reading and writing operation uses signed 32-bit integer to indicate
+ * the size.
+ *
+ *
+ */
+
+/**
+ * These enumerations are used when opening file. Values PJ_O_RDONLY,
+ * PJ_O_WRONLY, and PJ_O_RDWR are mutually exclusive. Value PJ_O_APPEND
+ * can only be used when the file is opened for writing. 
+ */
+enum pj_file_access
+{
+    PJ_O_RDONLY     = 0x1101,   /**< Open file for reading.             */
+    PJ_O_WRONLY     = 0x1102,   /**< Open file for writing.             */
+    PJ_O_RDWR       = 0x1103,   /**< Open file for reading and writing. 
+                                     File will be truncated.            */
+    PJ_O_APPEND     = 0x1108,   /**< Append to existing file.           */
+};
+
+/**
+ * The seek directive when setting the file position with #pj_file_setpos.
+ */
+enum pj_file_seek_type
+{
+    PJ_SEEK_SET     = 0x1201,   /**< Offset from beginning of the file. */
+    PJ_SEEK_CUR     = 0x1202,   /**< Offset from current position.      */
+    PJ_SEEK_END     = 0x1203,   /**< Size of the file plus offset.      */
+};
+
+/**
+ * Open the file as specified in \c pathname with the specified
+ * mode, and return the handle in \c fd. All files will be opened
+ * as binary.
+ *
+ * @param pool          Pool to allocate memory for the new file descriptor.
+ * @param pathname      The file name to open.
+ * @param flags         Open flags, which is bitmask combination of
+ *                      #pj_file_access enum. The flag must be either
+ *                      PJ_O_RDONLY, PJ_O_WRONLY, or PJ_O_RDWR. When file
+ *                      writing is specified, existing file will be 
+ *                      truncated unless PJ_O_APPEND is specified.
+ * @param fd            The returned descriptor.
+ *
+ * @return              PJ_SUCCESS or the appropriate error code on error.
+ */
+PJ_DECL(pj_status_t) pj_file_open(pj_pool_t *pool,
+                                  const char *pathname, 
+                                  unsigned flags,
+                                  pj_oshandle_t *fd);
+
+/**
+ * Close an opened file descriptor.
+ *
+ * @param fd            The file descriptor.
+ *
+ * @return              PJ_SUCCESS or the appropriate error code on error.
+ */
+PJ_DECL(pj_status_t) pj_file_close(pj_oshandle_t fd);
+
+/**
+ * Write data with the specified size to an opened file.
+ *
+ * @param fd            The file descriptor.
+ * @param data          Data to be written to the file.
+ * @param size          On input, specifies the size of data to be written.
+ *                      On return, it contains the number of data actually
+ *                      written to the file.
+ *
+ * @return              PJ_SUCCESS or the appropriate error code on error.
+ */
+PJ_DECL(pj_status_t) pj_file_write(pj_oshandle_t fd,
+                                   const void *data,
+                                   pj_ssize_t *size);
+
+/**
+ * Read data from the specified file. When end-of-file condition is set,
+ * this function will return PJ_SUCCESS but the size will contain zero.
+ *
+ * @param fd            The file descriptor.
+ * @param data          Pointer to buffer to receive the data.
+ * @param size          On input, specifies the maximum number of data to
+ *                      read from the file. On output, it contains the size
+ *                      of data actually read from the file. It will contain
+ *                      zero when EOF occurs.
+ *
+ * @return              PJ_SUCCESS or the appropriate error code on error.
+ *                      When EOF occurs, the return is PJ_SUCCESS but size
+ *                      will report zero.
+ */
+PJ_DECL(pj_status_t) pj_file_read(pj_oshandle_t fd,
+                                  void *data,
+                                  pj_ssize_t *size);
+
+/**
+ * Set file position to new offset according to directive \c whence.
+ *
+ * @param fd            The file descriptor.
+ * @param offset        The new file position to set.
+ * @param whence        The directive.
+ *
+ * @return              PJ_SUCCESS or the appropriate error code on error.
+ */
+PJ_DECL(pj_status_t) pj_file_setpos(pj_oshandle_t fd,
+                                    pj_off_t offset,
+                                    enum pj_file_seek_type whence);
+
+/**
+ * Get current file position.
+ *
+ * @param fd            The file descriptor.
+ * @param pos           On return contains the file position as measured
+ *                      from the beginning of the file.
+ *
+ * @return              PJ_SUCCESS or the appropriate error code on error.
+ */
+PJ_DECL(pj_status_t) pj_file_getpos(pj_oshandle_t fd,
+                                    pj_off_t *pos);
+
+/** @} */
+
+
+PJ_END_DECL
+
+#endif  /* __PJ_FILE_IO_H__ */
+
Index: pjproject/main/pjlib/include/pj/ioqueue.h
===================================================================
--- pjproject/main/pjlib/include/pj/ioqueue.h	(revision 17)
+++ pjproject/main/pjlib/include/pj/ioqueue.h	(revision 18)
@@ -15,6 +15,6 @@
 
 /**
- * @defgroup PJ_IO Network I/O
- * @brief Network I/O
+ * @defgroup PJ_IO Input/Output
+ * @brief Input/Output
  * @ingroup PJ_OS
  *
@@ -53,4 +53,9 @@
  * completed.
  *
+ * The I/O Queue can work on both socket and file descriptors. For 
+ * asynchronous file operations however, one must make sure that the correct
+ * file I/O back-end is used, because not all file I/O back-end can be
+ * used with the ioqueue. Please see \ref PJ_FILE_IO for more details.
+ *
  * The framework works natively in platforms where asynchronous operation API
  * exists, such as in Windows NT with IoCompletionPort/IOCP. In other 
@@ -61,5 +66,5 @@
  * The I/O queue provides more than just unified abstraction. It also:
  *  - makes sure that the operation uses the most effective way to utilize
- *    the underlying mechanism, to provide the maximum theoritical
+ *    the underlying mechanism, to achieve the maximum theoritical
  *    throughput possible on a given platform.
  *  - choose the most efficient mechanism for event polling on a given
@@ -488,5 +493,4 @@
 					  int *addrlen);
 
-
 /**
  * Instruct the I/O Queue to write to the handle. This function will return
@@ -530,6 +534,10 @@
 
 /**
- * This function behaves similarly as #pj_ioqueue_write(), except that
- * pj_sock_sendto() (or equivalent) will be called to send the data.
+ * Instruct the I/O Queue to write to the handle. This function will return
+ * immediately (i.e. non-blocking) regardless whether some data has been 
+ * transfered. If the function can't complete immediately, the caller will
+ * be notified about the completion when it calls pj_ioqueue_poll(). If 
+ * operation completes immediately and data has been transfered, the function
+ * returns PJ_SUCCESS and the callback will NOT be called.
  *
  * @param key	    the key that identifies the handle.
Index: pjproject/main/pjlib/include/pj/list.h
===================================================================
--- pjproject/main/pjlib/include/pj/list.h	(revision 17)
+++ pjproject/main/pjlib/include/pj/list.h	(revision 18)
@@ -46,6 +46,9 @@
  * @hideinitializer
  */
-#define PJ_DECL_LIST_MEMBER(type)  type *prev; /** List @a prev. */ \
-                                   type *next  /** List @a next. */ 
+#define PJ_DECL_LIST_MEMBER(type)                       \
+                                   /** List @a prev. */ \
+                                   type *prev;          \
+                                   /** List @a next. */ \
+                                   type *next 
 
 
Index: pjproject/main/pjlib/include/pj/pool.h
===================================================================
--- pjproject/main/pjlib/include/pj/pool.h	(revision 17)
+++ pjproject/main/pjlib/include/pj/pool.h	(revision 18)
@@ -127,5 +127,5 @@
 struct pj_pool_t
 {
-    PJ_DECL_LIST_MEMBER(struct pj_pool_t);
+    PJ_DECL_LIST_MEMBER(struct pj_pool_t);  /**< Standard list elements.    */
 
     /** Pool name */
Index: pjproject/main/pjlib/include/pj/types.h
===================================================================
--- pjproject/main/pjlib/include/pj/types.h	(revision 17)
+++ pjproject/main/pjlib/include/pj/types.h	(revision 18)
@@ -64,4 +64,12 @@
 #define PJ_FALSE    0
 
+/**
+ * File offset type.
+ */
+#if defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0
+typedef pj_int64_t pj_off_t;
+#else
+typedef pj_ssize_t pj_off_t;
+#endif
 
 ///////////////////////////////////////////////////////////////////////////////
Index: pjproject/main/pjlib/include/pj/xml.h
===================================================================
--- pjproject/main/pjlib/include/pj/xml.h	(revision 17)
+++ pjproject/main/pjlib/include/pj/xml.h	(revision 18)
@@ -31,7 +31,7 @@
 struct pj_xml_attr
 {
-    PJ_DECL_LIST_MEMBER(pj_xml_attr);
-    pj_str_t	name;	    /**< Attribute name. */
-    pj_str_t	value;	    /**< Attribute value. */
+    PJ_DECL_LIST_MEMBER(pj_xml_attr);   /**< Standard list elements.    */
+    pj_str_t	name;	                /**< Attribute name.            */
+    pj_str_t	value;	                /**< Attribute value.           */
 };
 
@@ -40,5 +40,5 @@
 typedef struct pj_xml_node_head
 {
-    PJ_DECL_LIST_MEMBER(pj_xml_node);
+    PJ_DECL_LIST_MEMBER(pj_xml_node);   /**< Standard list elements.    */
 } pj_xml_node_head;
 
@@ -46,9 +46,9 @@
 struct pj_xml_node
 {
-    PJ_DECL_LIST_MEMBER(pj_xml_node);   /** List @a prev and @a next member */
-    pj_str_t		name;		/** Node name. */
-    pj_xml_attr		attr_head;      /** Attribute list. */
-    pj_xml_node_head	node_head;      /** Node list. */
-    pj_str_t		content;	/** Node content. */
+    PJ_DECL_LIST_MEMBER(pj_xml_node);   /**< List @a prev and @a next member */
+    pj_str_t		name;		/**< Node name.                      */
+    pj_xml_attr		attr_head;      /**< Attribute list.                 */
+    pj_xml_node_head	node_head;      /**< Node list.                      */
+    pj_str_t		content;	/**< Node content.                   */
 };
 
Index: pjproject/main/pjlib/include/pjlib.h
===================================================================
--- pjproject/main/pjlib/include/pjlib.h	(revision 17)
+++ pjproject/main/pjlib/include/pjlib.h	(revision 18)
@@ -17,4 +17,6 @@
 #include <pj/except.h>
 #include <pj/fifobuf.h>
+#include <pj/file_access.h>
+#include <pj/file_io.h>
 #include <pj/guid.h>
 #include <pj/hash.h>
Index: pjproject/main/pjlib/src/pj/file_access_win32.c
===================================================================
--- pjproject/main/pjlib/src/pj/file_access_win32.c	(revision 18)
+++ pjproject/main/pjlib/src/pj/file_access_win32.c	(revision 18)
@@ -0,0 +1,172 @@
+/* $Id$ */
+#include <pj/file_access.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <windows.h>
+#include <time.h>
+
+/*
+ * pj_file_exists()
+ */
+PJ_DEF(pj_bool_t) pj_file_exists(const char *filename)
+{
+    HANDLE hFile;
+
+    PJ_ASSERT_RETURN(filename != NULL, 0);
+
+    hFile = CreateFile(filename, READ_CONTROL, FILE_SHARE_READ, NULL,
+                       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (hFile == INVALID_HANDLE_VALUE)
+        return 0;
+
+    CloseHandle(hFile);
+    return PJ_TRUE;
+}
+
+
+/*
+ * pj_file_size()
+ */
+PJ_DEF(pj_off_t) pj_file_size(const char *filename)
+{
+    HANDLE hFile;
+    DWORD sizeLo, sizeHi;
+    pj_off_t size;
+
+    PJ_ASSERT_RETURN(filename != NULL, -1);
+
+    hFile = CreateFile(filename, READ_CONTROL, 
+                       FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (hFile == INVALID_HANDLE_VALUE)
+        return -1;
+
+    sizeLo = GetFileSize(hFile, &sizeHi);
+    if (sizeLo == INVALID_FILE_SIZE) {
+        DWORD dwStatus = GetLastError();
+        if (dwStatus != NO_ERROR) {
+            CloseHandle(hFile);
+            return -1;
+        }
+    }
+
+    size = sizeHi;
+    size = (size << 32) + sizeLo;
+
+    CloseHandle(hFile);
+    return size;
+}
+
+
+/*
+ * pj_file_delete()
+ */
+PJ_DEF(pj_status_t) pj_file_delete(const char *filename)
+{
+    PJ_ASSERT_RETURN(filename != NULL, PJ_EINVAL);
+
+    if (DeleteFile(filename) == FALSE)
+        return PJ_RETURN_OS_ERROR(GetLastError());
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * pj_file_move()
+ */
+PJ_DEF(pj_status_t) pj_file_move( const char *oldname, const char *newname)
+{
+    BOOL rc;
+
+    PJ_ASSERT_RETURN(oldname!=NULL && newname!=NULL, PJ_EINVAL);
+
+#if PJ_WIN32_WINNT >= 0x0400
+    rc = MoveFileEx(oldname, newname, 
+                    MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING);
+#else
+    rc = MoveFile(oldname, newname);
+#endif
+
+    if (!rc)
+        return PJ_RETURN_OS_ERROR(GetLastError());
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t file_time_to_time_val(const FILETIME *file_time,
+                                         pj_time_val *time_val)
+{
+    SYSTEMTIME systemTime, localTime;
+    struct tm tm;
+
+    if (!FileTimeToSystemTime(file_time, &systemTime))
+        return -1;
+
+    if (!SystemTimeToTzSpecificLocalTime(NULL, &systemTime, &localTime))
+        return -1;
+
+    memset(&tm, 0, sizeof(struct tm));
+    tm.tm_year = localTime.wYear - 1900;
+    tm.tm_mon = localTime.wMonth - 1;
+    tm.tm_mday = localTime.wDay;
+    tm.tm_hour = localTime.wHour;
+    tm.tm_min = localTime.wMinute;
+    tm.tm_sec = localTime.wSecond;
+    tm.tm_isdst = 0;
+
+    time_val->sec = mktime(&tm);
+    if (time_val->sec == (time_t)-1)
+        return -1;
+
+    time_val->msec = localTime.wMilliseconds;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_file_getstat()
+ */
+PJ_DEF(pj_status_t) pj_file_getstat(const char *filename, pj_file_stat *stat)
+{
+    HANDLE hFile;
+    DWORD sizeLo, sizeHi;
+    FILETIME creationTime, accessTime, writeTime;
+
+    PJ_ASSERT_RETURN(filename!=NULL && stat!=NULL, PJ_EINVAL);
+
+    hFile = CreateFile(filename, READ_CONTROL, FILE_SHARE_READ, NULL,
+                       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (hFile == INVALID_HANDLE_VALUE)
+        return PJ_RETURN_OS_ERROR(GetLastError());
+
+    sizeLo = GetFileSize(hFile, &sizeHi);
+    if (sizeLo == INVALID_FILE_SIZE) {
+        DWORD dwStatus = GetLastError();
+        if (dwStatus != NO_ERROR) {
+            CloseHandle(hFile);
+            return PJ_RETURN_OS_ERROR(dwStatus);
+        }
+    }
+
+    stat->size = sizeHi;
+    stat->size = (stat->size << 32) + sizeLo;
+
+    if (GetFileTime(hFile, &creationTime, &accessTime, &writeTime)==FALSE) {
+        DWORD dwStatus = GetLastError();
+        CloseHandle(hFile);
+        return PJ_RETURN_OS_ERROR(dwStatus);
+    }
+
+    CloseHandle(hFile);
+
+    if (file_time_to_time_val(&creationTime, &stat->ctime) != PJ_SUCCESS)
+        return PJ_RETURN_OS_ERROR(GetLastError());
+
+    file_time_to_time_val(&accessTime, &stat->atime);
+    file_time_to_time_val(&writeTime, &stat->mtime);
+
+    return PJ_SUCCESS;
+}
+
Index: pjproject/main/pjlib/src/pj/file_io_ansi.c
===================================================================
--- pjproject/main/pjlib/src/pj/file_io_ansi.c	(revision 18)
+++ pjproject/main/pjlib/src/pj/file_io_ansi.c	(revision 18)
@@ -0,0 +1,138 @@
+/* $Id$ */
+#include <pj/file_io.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <stdio.h>
+#include <errno.h>
+
+PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool,
+                                  const char *pathname, 
+                                  unsigned flags,
+                                  pj_oshandle_t *fd)
+{
+    char mode[8];
+    char *p = mode;
+
+    PJ_ASSERT_RETURN(pathname && fd, PJ_EINVAL);
+    PJ_UNUSED_ARG(pool);
+
+    if ((flags & PJ_O_APPEND) == PJ_O_APPEND) {
+        if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) {
+            *p++ = 'a';
+            if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY)
+                *p++ = '+';
+        } else {
+            /* This is invalid.
+             * Can not specify PJ_O_RDONLY with PJ_O_APPEND! 
+             */
+        }
+    } else {
+        if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY) {
+            *p++ = 'r';
+            if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY)
+                *p++ = '+';
+        } else {
+            *p++ = 'w';
+        }
+    }
+
+    if (p==mode)
+        return PJ_EINVAL;
+
+    *p++ = 'b';
+    *p++ = '\0';
+
+    *fd = fopen(pathname, mode);
+    if (*fd == NULL)
+        return PJ_RETURN_OS_ERROR(errno);
+    
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_close(pj_oshandle_t fd)
+{
+    PJ_ASSERT_RETURN(fd, PJ_EINVAL);
+    if (fclose((FILE*)fd) != 0)
+        return PJ_RETURN_OS_ERROR(errno);
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_write( pj_oshandle_t fd,
+                                   const void *data,
+                                   pj_ssize_t *size)
+{
+    size_t written;
+
+    clearerr((FILE*)fd);
+    written = fwrite(data, 1, *size, (FILE*)fd);
+    if (ferror((FILE*)fd)) {
+        return PJ_RETURN_OS_ERROR(errno);
+    }
+
+    *size = written;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_read( pj_oshandle_t fd,
+                                  void *data,
+                                  pj_ssize_t *size)
+{
+    size_t bytes;
+
+    clearerr((FILE*)fd);
+    bytes = fread(data, 1, *size, (FILE*)fd);
+    if (ferror((FILE*)fd)) {
+        return PJ_RETURN_OS_ERROR(errno);
+    }
+
+    *size = bytes;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_bool_t) pj_file_eof(pj_oshandle_t fd, enum pj_file_access access)
+{
+    PJ_UNUSED_ARG(access);
+    return feof((FILE*)fd) ? PJ_TRUE : 0;
+}
+
+PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd,
+                                    pj_off_t offset,
+                                    enum pj_file_seek_type whence)
+{
+    int mode;
+
+    switch (whence) {
+    case PJ_SEEK_SET:
+        mode = SEEK_SET; break;
+    case PJ_SEEK_CUR:
+        mode = SEEK_CUR; break;
+    case PJ_SEEK_END:
+        mode = SEEK_END; break;
+    default:
+        pj_assert(!"Invalid whence in file_setpos");
+        return PJ_EINVAL;
+    }
+
+    if (fseek((FILE*)fd, (long)offset, mode) != 0)
+        return PJ_RETURN_OS_ERROR(errno);
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_getpos( pj_oshandle_t fd,
+                                    pj_off_t *pos)
+{
+    long offset;
+
+    offset = ftell((FILE*)fd);
+    if (offset == -1) {
+        *pos = -1;
+        return PJ_RETURN_OS_ERROR(errno);
+    }
+
+    *pos = offset;
+    return PJ_SUCCESS;
+}
+
+
Index: pjproject/main/pjlib/src/pj/file_io_win32.c
===================================================================
--- pjproject/main/pjlib/src/pj/file_io_win32.c	(revision 18)
+++ pjproject/main/pjlib/src/pj/file_io_win32.c	(revision 18)
@@ -0,0 +1,186 @@
+/* $Id$ */
+#include <pj/file_io.h>
+#include <pj/errno.h>
+#include <pj/assert.h>
+
+#include <windows.h>
+
+#ifndef INVALID_SET_FILE_POINTER
+#   define INVALID_SET_FILE_POINTER     ((DWORD)-1)
+#endif
+
+/**
+ * Check for end-of-file condition on the specified descriptor.
+ *
+ * @param fd            The file descriptor.
+ * @param access        The desired access.
+ *
+ * @return              Non-zero if file is EOF.
+ */
+PJ_DECL(pj_bool_t) pj_file_eof(pj_oshandle_t fd, 
+                               enum pj_file_access access);
+
+
+PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool,
+                                  const char *pathname, 
+                                  unsigned flags,
+                                  pj_oshandle_t *fd)
+{
+    HANDLE hFile;
+    DWORD dwDesiredAccess = 0;
+    DWORD dwShareMode = 0;
+    DWORD dwCreationDisposition = 0;
+    DWORD dwFlagsAndAttributes = 0;
+
+    PJ_UNUSED_ARG(pool);
+
+    PJ_ASSERT_RETURN(pathname!=NULL, PJ_EINVAL);
+
+    if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) {
+        dwDesiredAccess |= GENERIC_WRITE;
+        if ((flags & PJ_O_APPEND) == PJ_O_APPEND) {
+            dwDesiredAccess |= FILE_APPEND_DATA;
+        } else {
+            dwDesiredAccess &= ~(FILE_APPEND_DATA);
+            dwCreationDisposition |= CREATE_ALWAYS;
+        }
+    }
+    if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY) {
+        dwDesiredAccess |= GENERIC_READ;
+        if (flags == PJ_O_RDONLY)
+            dwCreationDisposition |= OPEN_EXISTING;
+    }
+
+    if (dwDesiredAccess == 0) {
+        pj_assert(!"Invalid file open flags");
+        return PJ_EINVAL;
+    }
+
+    dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+    dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
+
+    hFile = CreateFile(pathname, dwDesiredAccess, dwShareMode, NULL,
+                       dwCreationDisposition, dwFlagsAndAttributes, NULL);
+    if (hFile == INVALID_HANDLE_VALUE) {
+        *fd = 0;
+        return PJ_RETURN_OS_ERROR(GetLastError());
+    }
+
+    *fd = hFile;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_close(pj_oshandle_t fd)
+{
+    if (CloseHandle(fd)==0)
+        return PJ_RETURN_OS_ERROR(GetLastError());
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_write( pj_oshandle_t fd,
+                                   const void *data,
+                                   pj_ssize_t *size)
+{
+    BOOL rc;
+    DWORD bytesWritten;
+
+    rc = WriteFile(fd, data, *size, &bytesWritten, NULL);
+    if (!rc) {
+        *size = -1;
+        return PJ_RETURN_OS_ERROR(GetLastError());
+    }
+
+    *size = bytesWritten;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_read( pj_oshandle_t fd,
+                                  void *data,
+                                  pj_ssize_t *size)
+{
+    BOOL rc;
+    DWORD bytesRead;
+
+    rc = ReadFile(fd, data, *size, &bytesRead, NULL);
+    if (!rc) {
+        *size = -1;
+        return PJ_RETURN_OS_ERROR(GetLastError());
+    }
+
+    *size = bytesRead;
+    return PJ_SUCCESS;
+}
+
+/*
+PJ_DEF(pj_bool_t) pj_file_eof(pj_oshandle_t fd, enum pj_file_access access)
+{
+    BOOL rc;
+    DWORD dummy = 0, bytes;
+    DWORD dwStatus;
+
+    if ((access & PJ_O_RDONLY) == PJ_O_RDONLY) {
+        rc = ReadFile(fd, &dummy, 0, &bytes, NULL);
+    } else if ((access & PJ_O_WRONLY) == PJ_O_WRONLY) {
+        rc = WriteFile(fd, &dummy, 0, &bytes, NULL);
+    } else {
+        pj_assert(!"Invalid access");
+        return PJ_TRUE;
+    }
+
+    dwStatus = GetLastError();
+    if (dwStatus==ERROR_HANDLE_EOF)
+        return PJ_TRUE;
+
+    return 0;
+}
+*/
+
+PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd,
+                                    pj_off_t offset,
+                                    enum pj_file_seek_type whence)
+{
+    DWORD dwMoveMethod;
+    DWORD dwNewPos;
+    LONG  hi32;
+
+    if (whence == PJ_SEEK_SET)
+        dwMoveMethod = FILE_BEGIN;
+    else if (whence == PJ_SEEK_CUR)
+        dwMoveMethod = FILE_CURRENT;
+    else if (whence == PJ_SEEK_END)
+        dwMoveMethod = FILE_END;
+    else {
+        pj_assert(!"Invalid whence in file_setpos");
+        return PJ_EINVAL;
+    }
+
+    hi32 = (LONG)(offset >> 32);
+    dwNewPos = SetFilePointer(fd, (long)offset, &hi32, dwMoveMethod);
+    if (dwNewPos == (DWORD)INVALID_SET_FILE_POINTER) {
+        DWORD dwStatus = GetLastError();
+        if (dwStatus != 0)
+            return PJ_RETURN_OS_ERROR(dwStatus);
+        /* dwNewPos actually is not an error. */
+    }
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_getpos( pj_oshandle_t fd,
+                                    pj_off_t *pos)
+{
+    LONG hi32 = 0;
+    DWORD lo32;
+
+    lo32 = SetFilePointer(fd, 0, &hi32, FILE_CURRENT);
+    if (lo32 == (DWORD)INVALID_SET_FILE_POINTER) {
+        DWORD dwStatus = GetLastError();
+        if (dwStatus != 0)
+            return PJ_RETURN_OS_ERROR(dwStatus);
+    }
+
+    *pos = hi32;
+    *pos = (*pos << 32) + lo32;
+    return PJ_SUCCESS;
+}
+
Index: pjproject/main/pjlib/src/pj/ioqueue_winnt.c
===================================================================
--- pjproject/main/pjlib/src/pj/ioqueue_winnt.c	(revision 17)
+++ pjproject/main/pjlib/src/pj/ioqueue_winnt.c	(revision 18)
@@ -36,5 +36,5 @@
 
 /*
- * OVERLAP structure for send and receive.
+ * OVERLAPPPED structure for send and receive.
  */
 typedef struct ioqueue_overlapped
@@ -76,4 +76,12 @@
 };
 
+/* Type of handle in the key. */
+enum handle_type
+{
+    HND_IS_UNKNOWN,
+    HND_IS_FILE,
+    HND_IS_SOCKET,
+};
+
 /*
  * Structure for individual socket.
@@ -84,4 +92,5 @@
     HANDLE		hnd;
     void	       *user_data;
+    enum handle_type    hnd_type;
 #if PJ_HAS_TCP
     int			connecting;
@@ -318,4 +327,5 @@
     rec->ioqueue = ioqueue;
     rec->hnd = (HANDLE)sock;
+    rec->hnd_type = HND_IS_SOCKET;
     rec->user_data = user_data;
     pj_memcpy(&rec->cb, cb, sizeof(pj_ioqueue_callback));
@@ -364,4 +374,7 @@
     }
 #endif
+    if (key->hnd_type == HND_IS_FILE) {
+        CloseHandle(key->hnd);
+    }
     return PJ_SUCCESS;
 }
@@ -506,5 +519,5 @@
 
     PJ_CHECK_STACK();
-    PJ_ASSERT_RETURN(key && op_key && buffer, PJ_EINVAL);
+    PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL);
 
     op_key_rec = (union operation_key*)op_key->internal__;
Index: pjproject/main/pjlib/src/pjlib-samples/list.c
===================================================================
--- pjproject/main/pjlib/src/pjlib-samples/list.c	(revision 17)
+++ pjproject/main/pjlib/src/pjlib-samples/list.c	(revision 18)
@@ -16,5 +16,5 @@
 {
     // This must be the first member declared in the struct!
-    PJ_DECL_LIST_MEMBER(struct my_node)
+    PJ_DECL_LIST_MEMBER(struct my_node);
     int value;
 };
Index: pjproject/main/pjlib/src/pjlib-test/file.c
===================================================================
--- pjproject/main/pjlib/src/pjlib-test/file.c	(revision 18)
+++ pjproject/main/pjlib/src/pjlib-test/file.c	(revision 18)
@@ -0,0 +1,192 @@
+/* $Id$ */
+#include "test.h"
+#include <pjlib.h>
+
+#if INCLUDE_FILE_TEST
+
+#define FILENAME                "testfil1.txt"
+#define NEWNAME                 "testfil2.txt"
+#define INCLUDE_FILE_TIME_TEST  0
+
+static char buffer[11] = "Hello world";
+
+int file_test(void)
+{
+    enum { FILE_MAX_AGE = 1000 };
+    pj_oshandle_t fd = 0;
+    pj_status_t status;
+    char readbuf[sizeof(buffer)+16];
+    pj_file_stat stat;
+    pj_time_val start_time;
+    pj_ssize_t size;
+    pj_off_t pos;
+
+    PJ_LOG(3,("", "..file io test.."));
+
+    /* Get time. */
+    pj_gettimeofday(&start_time);
+
+    /* Delete original file if exists. */
+    if (pj_file_exists(FILENAME))
+        pj_file_delete(FILENAME);
+
+    /*
+     * Write data to the file.
+     */
+    status = pj_file_open(NULL, FILENAME, PJ_O_WRONLY, &fd);
+    if (status != PJ_SUCCESS) {
+        app_perror("...file_open() error", status);
+        return -10;
+    }
+
+    size = sizeof(buffer);
+    status = pj_file_write(fd, buffer, &size);
+    if (status != PJ_SUCCESS) {
+        app_perror("...file_write() error", status);
+        pj_file_close(fd);
+        return -20;
+    }
+    if (size != sizeof(buffer))
+        return -25;
+
+    status = pj_file_close(fd);
+    if (status != PJ_SUCCESS) {
+        app_perror("...file_close() error", status);
+        return -30;
+    }
+
+    /* Check the file existance and size. */
+    if (!pj_file_exists(FILENAME))
+        return -40;
+
+    if (pj_file_size(FILENAME) != sizeof(buffer))
+        return -50;
+
+    /* Get file stat. */
+    status = pj_file_getstat(FILENAME, &stat);
+    if (status != PJ_SUCCESS)
+        return -60;
+
+    /* Check stat size. */
+    if (stat.size != sizeof(buffer))
+        return -70;
+
+    /* Check file creation time >= start_time. */
+    if (!PJ_TIME_VAL_GTE(stat.ctime, start_time))
+#if INCLUDE_FILE_TIME_TEST
+        return -80;
+    /* Check file creation time is not much later. */
+    PJ_TIME_VAL_SUB(stat.ctime, start_time);
+    if (stat.ctime.sec > FILE_MAX_AGE)
+        return -90;
+
+    /* Check file modification time >= start_time. */
+    if (!PJ_TIME_VAL_GTE(stat.mtime, start_time))
+        return -80;
+    /* Check file modification time is not much later. */
+    PJ_TIME_VAL_SUB(stat.mtime, start_time);
+    if (stat.mtime.sec > FILE_MAX_AGE)
+        return -90;
+
+    /* Check file access time >= start_time. */
+    if (!PJ_TIME_VAL_GTE(stat.atime, start_time))
+        return -80;
+    /* Check file access time is not much later. */
+    PJ_TIME_VAL_SUB(stat.atime, start_time);
+    if (stat.atime.sec > FILE_MAX_AGE)
+        return -90;
+#endif
+
+    /*
+     * Re-open the file and read data.
+     */
+    status = pj_file_open(NULL, FILENAME, PJ_O_RDONLY, &fd);
+    if (status != PJ_SUCCESS) {
+        app_perror("...file_open() error", status);
+        return -100;
+    }
+
+    size = 0;
+    while (size < sizeof(readbuf)) {
+        pj_ssize_t read;
+        read = 1;
+        status = pj_file_read(fd, &readbuf[size], &read);
+        if (status != PJ_SUCCESS) {
+            app_perror("...file_read() error", status);
+            return -110;
+        }
+        if (read == 0) {
+            // EOF
+            break;
+        }
+        size += read;
+    }
+
+    if (size != sizeof(buffer))
+        return -120;
+
+    /*
+    if (!pj_file_eof(fd, PJ_O_RDONLY))
+        return -130;
+     */
+
+    if (pj_memcmp(readbuf, buffer, size) != 0)
+        return -140;
+
+    /* Seek test. */
+    status = pj_file_setpos(fd, 4, PJ_SEEK_SET);
+    if (status != PJ_SUCCESS) {
+        app_perror("...file_setpos() error", status);
+        return -141;
+    }
+
+    /* getpos test. */
+    status = pj_file_getpos(fd, &pos);
+    if (status != PJ_SUCCESS) {
+        app_perror("...file_getpos() error", status);
+        return -142;
+    }
+    if (pos != 4)
+        return -143;
+
+    status = pj_file_close(fd);
+    if (status != PJ_SUCCESS) {
+        app_perror("...file_close() error", status);
+        return -150;
+    }
+
+    /*
+     * Rename test.
+     */
+    status = pj_file_move(FILENAME, NEWNAME);
+    if (status != PJ_SUCCESS) {
+        app_perror("...file_move() error", status);
+        return -160;
+    }
+
+    if (pj_file_exists(FILENAME))
+        return -170;
+    if (!pj_file_exists(NEWNAME))
+        return -180;
+
+    if (pj_file_size(NEWNAME) != sizeof(buffer))
+        return -190;
+
+    /* Delete test. */
+    status = pj_file_delete(NEWNAME);
+    if (status != PJ_SUCCESS) {
+        app_perror("...file_delete() error", status);
+        return -200;
+    }
+
+    if (pj_file_exists(NEWNAME))
+        return -210;
+
+    PJ_LOG(3,("", "...success"));
+    return PJ_SUCCESS;
+}
+
+#else
+int dummy_file_test;
+#endif
+
Index: pjproject/main/pjlib/src/pjlib-test/test.c
===================================================================
--- pjproject/main/pjlib/src/pjlib-test/test.c	(revision 17)
+++ pjproject/main/pjlib/src/pjlib-test/test.c	(revision 18)
@@ -32,4 +32,8 @@
     mem = &caching_pool.factory;
 
+    pj_log_set_level(3);
+    pj_log_set_decor(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | 
+                     PJ_LOG_HAS_MICRO_SEC);
+
     rc = pj_init();
     if (rc != 0) {
@@ -38,6 +42,4 @@
     }
     
-    pj_log_set_level(3);
-    pj_log_set_decor(PJ_LOG_HAS_NEWLINE);
     pj_dump_config();
     pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 );
@@ -127,4 +129,8 @@
 #endif
 
+#if INCLUDE_FILE_TEST
+    DO_TEST( file_test() );
+#endif
+
 #if INCLUDE_XML_TEST
     DO_TEST( xml_test() );
Index: pjproject/main/pjlib/src/pjlib-test/test.h
===================================================================
--- pjproject/main/pjlib/src/pjlib-test/test.h	(revision 17)
+++ pjproject/main/pjlib/src/pjlib-test/test.h	(revision 18)
@@ -9,5 +9,6 @@
 #define GROUP_OS                    0
 #define GROUP_DATA_STRUCTURE        0
-#define GROUP_NETWORK               1
+#define GROUP_NETWORK               0
+#define GROUP_FILE                  1
 #define GROUP_EXTRA                 0
 
@@ -33,4 +34,5 @@
 #define INCLUDE_TCP_IOQUEUE_TEST    GROUP_NETWORK
 #define INCLUDE_IOQUEUE_PERF_TEST   GROUP_NETWORK
+#define INCLUDE_FILE_TEST           GROUP_FILE
 #define INCLUDE_XML_TEST	    GROUP_EXTRA
 
@@ -69,4 +71,5 @@
 extern int tcp_ioqueue_test(void);
 extern int ioqueue_perf_test(void);
+extern int file_test(void);
 extern int xml_test(void);
 
