diff options
Diffstat (limited to 'pjlib')
-rw-r--r-- | pjlib/build/os-win32.mak | 8 | ||||
-rw-r--r-- | pjlib/build/pjlib.dsp | 21 | ||||
-rw-r--r-- | pjlib/build/pjlib_test.dsp | 4 | ||||
-rw-r--r-- | pjlib/include/pj/addr_resolv.h | 2 | ||||
-rw-r--r-- | pjlib/include/pj/file_access.h | 91 | ||||
-rw-r--r-- | pjlib/include/pj/file_io.h | 155 | ||||
-rw-r--r-- | pjlib/include/pj/ioqueue.h | 20 | ||||
-rw-r--r-- | pjlib/include/pj/list.h | 7 | ||||
-rw-r--r-- | pjlib/include/pj/pool.h | 2 | ||||
-rw-r--r-- | pjlib/include/pj/types.h | 8 | ||||
-rw-r--r-- | pjlib/include/pj/xml.h | 18 | ||||
-rw-r--r-- | pjlib/include/pjlib.h | 4 | ||||
-rw-r--r-- | pjlib/src/pj/file_access_win32.c | 172 | ||||
-rw-r--r-- | pjlib/src/pj/file_io_ansi.c | 138 | ||||
-rw-r--r-- | pjlib/src/pj/file_io_win32.c | 186 | ||||
-rw-r--r-- | pjlib/src/pj/ioqueue_winnt.c | 29 | ||||
-rw-r--r-- | pjlib/src/pjlib-samples/list.c | 2 | ||||
-rw-r--r-- | pjlib/src/pjlib-test/file.c | 192 | ||||
-rw-r--r-- | pjlib/src/pjlib-test/test.c | 10 | ||||
-rw-r--r-- | pjlib/src/pjlib-test/test.h | 9 |
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); |