summaryrefslogtreecommitdiff
path: root/pjlib
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2005-11-07 15:47:28 +0000
committerBenny Prijono <bennylp@teluu.com>2005-11-07 15:47:28 +0000
commite25a988d098a075f5519090c24237c3b97bc1323 (patch)
treee6f4110d717550912cc25bb4e016bd60177d0016 /pjlib
parent4539be1bf3b88417e3b52fdbeb17d6a74266cb4a (diff)
Added file I/O and file access API
git-svn-id: http://svn.pjsip.org/repos/pjproject/main@18 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjlib')
-rw-r--r--pjlib/build/os-win32.mak8
-rw-r--r--pjlib/build/pjlib.dsp21
-rw-r--r--pjlib/build/pjlib_test.dsp4
-rw-r--r--pjlib/include/pj/addr_resolv.h2
-rw-r--r--pjlib/include/pj/file_access.h91
-rw-r--r--pjlib/include/pj/file_io.h155
-rw-r--r--pjlib/include/pj/ioqueue.h20
-rw-r--r--pjlib/include/pj/list.h7
-rw-r--r--pjlib/include/pj/pool.h2
-rw-r--r--pjlib/include/pj/types.h8
-rw-r--r--pjlib/include/pj/xml.h18
-rw-r--r--pjlib/include/pjlib.h4
-rw-r--r--pjlib/src/pj/file_access_win32.c172
-rw-r--r--pjlib/src/pj/file_io_ansi.c138
-rw-r--r--pjlib/src/pj/file_io_win32.c186
-rw-r--r--pjlib/src/pj/ioqueue_winnt.c29
-rw-r--r--pjlib/src/pjlib-samples/list.c2
-rw-r--r--pjlib/src/pjlib-test/file.c192
-rw-r--r--pjlib/src/pjlib-test/test.c10
-rw-r--r--pjlib/src/pjlib-test/test.h9
20 files changed, 1043 insertions, 35 deletions
diff --git a/pjlib/build/os-win32.mak b/pjlib/build/os-win32.mak
index 7b236c6a..27e3f5e9 100644
--- a/pjlib/build/os-win32.mak
+++ b/pjlib/build/os-win32.mak
@@ -7,12 +7,18 @@
# (the library) for this specific operating system. Object files common
# 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
+
#
# TEST_OBJS are operating system specific object files to be included in
# the test application.
diff --git a/pjlib/build/pjlib.dsp b/pjlib/build/pjlib.dsp
index 8d84a5c1..585a138d 100644
--- a/pjlib/build/pjlib.dsp
+++ b/pjlib/build/pjlib.dsp
@@ -194,6 +194,19 @@ SOURCE=..\src\pj\fifobuf.c
# End Source File
# 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
# Begin Source File
@@ -476,6 +489,14 @@ 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
+
SOURCE=..\include\pj\guid.h
# End Source File
# Begin Source File
diff --git a/pjlib/build/pjlib_test.dsp b/pjlib/build/pjlib_test.dsp
index 9aff9958..c2dc04ab 100644
--- a/pjlib/build/pjlib_test.dsp
+++ b/pjlib/build/pjlib_test.dsp
@@ -107,6 +107,10 @@ SOURCE="..\src\pjlib-test\fifobuf.c"
# End Source File
# 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
# Begin Source File
diff --git a/pjlib/include/pj/addr_resolv.h b/pjlib/include/pj/addr_resolv.h
index 1a47cf0d..1da92082 100644
--- a/pjlib/include/pj/addr_resolv.h
+++ b/pjlib/include/pj/addr_resolv.h
@@ -14,7 +14,7 @@
PJ_BEGIN_DECL
/**
- * @defgroup pj_addr_resolve Address Resolution
+ * @defgroup pj_addr_resolve Network Address Resolution
* @ingroup PJ_IO
* @{
*
diff --git a/pjlib/include/pj/file_access.h b/pjlib/include/pj/file_access.h
new file mode 100644
index 00000000..473484d8
--- /dev/null
+++ b/pjlib/include/pj/file_access.h
@@ -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__ */
diff --git a/pjlib/include/pj/file_io.h b/pjlib/include/pj/file_io.h
new file mode 100644
index 00000000..7e5fa3ea
--- /dev/null
+++ b/pjlib/include/pj/file_io.h
@@ -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__ */
+
diff --git a/pjlib/include/pj/ioqueue.h b/pjlib/include/pj/ioqueue.h
index ce30c9f9..303df162 100644
--- a/pjlib/include/pj/ioqueue.h
+++ b/pjlib/include/pj/ioqueue.h
@@ -14,8 +14,8 @@
PJ_BEGIN_DECL
/**
- * @defgroup PJ_IO Network I/O
- * @brief Network I/O
+ * @defgroup PJ_IO Input/Output
+ * @brief Input/Output
* @ingroup PJ_OS
*
* This section contains API building blocks to perform network I/O and
@@ -52,6 +52,11 @@ PJ_BEGIN_DECL
* asynchronous operation and to be notified later when the operation has
* 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
* platforms, the I/O queue abstracts the operating system's event poll API
@@ -60,7 +65,7 @@ PJ_BEGIN_DECL
*
* 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
* platform.
@@ -487,7 +492,6 @@ PJ_DECL(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key,
pj_sockaddr_t *addr,
int *addrlen);
-
/**
* 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
@@ -529,8 +533,12 @@ PJ_DECL(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key,
/**
- * 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.
* @param op_key An operation specific key to be associated with the
diff --git a/pjlib/include/pj/list.h b/pjlib/include/pj/list.h
index 95ae99fd..f6662eb6 100644
--- a/pjlib/include/pj/list.h
+++ b/pjlib/include/pj/list.h
@@ -45,8 +45,11 @@ PJ_BEGIN_DECL
* declares additional member @a prev and @a next to the structure.
* @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
/**
diff --git a/pjlib/include/pj/pool.h b/pjlib/include/pj/pool.h
index 4001079f..4be4d242 100644
--- a/pjlib/include/pj/pool.h
+++ b/pjlib/include/pj/pool.h
@@ -126,7 +126,7 @@ typedef struct pj_pool_block
*/
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 */
char obj_name[PJ_MAX_OBJ_NAME];
diff --git a/pjlib/include/pj/types.h b/pjlib/include/pj/types.h
index 892a508b..ec3df2c1 100644
--- a/pjlib/include/pj/types.h
+++ b/pjlib/include/pj/types.h
@@ -63,6 +63,14 @@ typedef int pj_bool_t;
/** False value. */
#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
///////////////////////////////////////////////////////////////////////////////
/*
diff --git a/pjlib/include/pj/xml.h b/pjlib/include/pj/xml.h
index 9777a514..fd7978ae 100644
--- a/pjlib/include/pj/xml.h
+++ b/pjlib/include/pj/xml.h
@@ -30,26 +30,26 @@ typedef struct pj_xml_node pj_xml_node;
/** This structure declares XML attribute. */
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. */
};
/** This structure describes XML node head inside XML node structure.
*/
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;
/** This structure describes XML node. */
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. */
};
/**
diff --git a/pjlib/include/pjlib.h b/pjlib/include/pjlib.h
index c5623d60..76d61d90 100644
--- a/pjlib/include/pjlib.h
+++ b/pjlib/include/pjlib.h
@@ -15,7 +15,9 @@
#include <pj/ctype.h>
#include <pj/errno.h>
#include <pj/except.h>
-#include <pj/fifobuf.h>
+#include <pj/fifobuf.h>
+#include <pj/file_access.h>
+#include <pj/file_io.h>
#include <pj/guid.h>
#include <pj/hash.h>
#include <pj/ioqueue.h>
diff --git a/pjlib/src/pj/file_access_win32.c b/pjlib/src/pj/file_access_win32.c
new file mode 100644
index 00000000..36405658
--- /dev/null
+++ b/pjlib/src/pj/file_access_win32.c
@@ -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;
+}
+
diff --git a/pjlib/src/pj/file_io_ansi.c b/pjlib/src/pj/file_io_ansi.c
new file mode 100644
index 00000000..f95c74a9
--- /dev/null
+++ b/pjlib/src/pj/file_io_ansi.c
@@ -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;
+}
+
+
diff --git a/pjlib/src/pj/file_io_win32.c b/pjlib/src/pj/file_io_win32.c
new file mode 100644
index 00000000..d9908410
--- /dev/null
+++ b/pjlib/src/pj/file_io_win32.c
@@ -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;
+}
+
diff --git a/pjlib/src/pj/ioqueue_winnt.c b/pjlib/src/pj/ioqueue_winnt.c
index afb75c54..32a74666 100644
--- a/pjlib/src/pj/ioqueue_winnt.c
+++ b/pjlib/src/pj/ioqueue_winnt.c
@@ -33,9 +33,9 @@ typedef struct generic_overlapped
WSAOVERLAPPED overlapped;
pj_ioqueue_operation_e operation;
} generic_overlapped;
-
+
/*
- * OVERLAP structure for send and receive.
+ * OVERLAPPPED structure for send and receive.
*/
typedef struct ioqueue_overlapped
{
@@ -74,6 +74,14 @@ union operation_key
ioqueue_accept_rec accept;
#endif
};
+
+/* Type of handle in the key. */
+enum handle_type
+{
+ HND_IS_UNKNOWN,
+ HND_IS_FILE,
+ HND_IS_SOCKET,
+};
/*
* Structure for individual socket.
@@ -82,7 +90,8 @@ struct pj_ioqueue_key_t
{
pj_ioqueue_t *ioqueue;
HANDLE hnd;
- void *user_data;
+ void *user_data;
+ enum handle_type hnd_type;
#if PJ_HAS_TCP
int connecting;
#endif
@@ -316,7 +325,8 @@ PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
/* Build the key for this socket. */
rec = pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t));
rec->ioqueue = ioqueue;
- rec->hnd = (HANDLE)sock;
+ rec->hnd = (HANDLE)sock;
+ rec->hnd_type = HND_IS_SOCKET;
rec->user_data = user_data;
pj_memcpy(&rec->cb, cb, sizeof(pj_ioqueue_callback));
@@ -336,7 +346,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
*key = rec;
return PJ_SUCCESS;
}
-
+
/*
* pj_ioqueue_unregister()
*/
@@ -362,7 +372,10 @@ PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key )
key->connecting = 0;
pj_lock_release(ioqueue->lock);
}
-#endif
+#endif
+ if (key->hnd_type == HND_IS_FILE) {
+ CloseHandle(key->hnd);
+ }
return PJ_SUCCESS;
}
@@ -482,7 +495,7 @@ PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout)
}
return -1;
}
-
+
/*
* pj_ioqueue_recv()
*
@@ -505,7 +518,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key,
union operation_key *op_key_rec;
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__;
op_key_rec->overlapped.wsabuf.buf = buffer;
diff --git a/pjlib/src/pjlib-samples/list.c b/pjlib/src/pjlib-samples/list.c
index 34a9fda2..127d8ddf 100644
--- a/pjlib/src/pjlib-samples/list.c
+++ b/pjlib/src/pjlib-samples/list.c
@@ -15,7 +15,7 @@
struct my_node
{
// 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;
};
diff --git a/pjlib/src/pjlib-test/file.c b/pjlib/src/pjlib-test/file.c
new file mode 100644
index 00000000..aa863506
--- /dev/null
+++ b/pjlib/src/pjlib-test/file.c
@@ -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
+
diff --git a/pjlib/src/pjlib-test/test.c b/pjlib/src/pjlib-test/test.c
index 8ccdc568..b65fa9e7 100644
--- a/pjlib/src/pjlib-test/test.c
+++ b/pjlib/src/pjlib-test/test.c
@@ -30,6 +30,10 @@ int test_inner(void)
int rc = 0;
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) {
@@ -37,8 +41,6 @@ int test_inner(void)
return rc;
}
- 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 );
@@ -125,6 +127,10 @@ int test_inner(void)
#if INCLUDE_IOQUEUE_PERF_TEST
DO_TEST( ioqueue_perf_test() );
#endif
+
+#if INCLUDE_FILE_TEST
+ DO_TEST( file_test() );
+#endif
#if INCLUDE_XML_TEST
DO_TEST( xml_test() );
diff --git a/pjlib/src/pjlib-test/test.h b/pjlib/src/pjlib-test/test.h
index f4408513..b16aff69 100644
--- a/pjlib/src/pjlib-test/test.h
+++ b/pjlib/src/pjlib-test/test.h
@@ -8,7 +8,8 @@
#define GROUP_LIBC 0
#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
#define INCLUDE_ERRNO_TEST GROUP_LIBC
@@ -31,7 +32,8 @@
#define INCLUDE_SELECT_TEST GROUP_NETWORK
#define INCLUDE_UDP_IOQUEUE_TEST GROUP_NETWORK
#define INCLUDE_TCP_IOQUEUE_TEST GROUP_NETWORK
-#define INCLUDE_IOQUEUE_PERF_TEST GROUP_NETWORK
+#define INCLUDE_IOQUEUE_PERF_TEST GROUP_NETWORK
+#define INCLUDE_FILE_TEST GROUP_FILE
#define INCLUDE_XML_TEST GROUP_EXTRA
#define INCLUDE_ECHO_SERVER 0
@@ -67,7 +69,8 @@ extern int sock_perf_test(void);
extern int select_test(void);
extern int udp_ioqueue_test(void);
extern int tcp_ioqueue_test(void);
-extern int ioqueue_perf_test(void);
+extern int ioqueue_perf_test(void);
+extern int file_test(void);
extern int xml_test(void);
extern int echo_server(void);