--- a/dio_posix.c
+++ b/dio_posix.c
@@ -1,659 +1,659 @@
-/*
-   +----------------------------------------------------------------------+
-   | PHP Version 5                                                        |
-   +----------------------------------------------------------------------+
-   | Copyright (c) 2009 Melanie Rhianna Lewis                             |
-   +----------------------------------------------------------------------+
-   | This source file is subject to version 3.0 of the PHP license,       |
-   | that is bundled with this package in the file LICENSE, and is        |
-   | available through the world-wide-web at the following url:           |
-   | http://www.php.net/license/3_0.txt.                                  |
-   | If you did not receive a copy of the PHP license and are unable to   |
-   | obtain it through the world-wide-web, please send a note to          |
-   | license@php.net so we can mail you a copy immediately.               |
-   +----------------------------------------------------------------------+
-   | Author: Melanie Rhianna Lewis <cyberspice@php.net>                   |
-   +----------------------------------------------------------------------+
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "php.h"
-
-#include "php_dio_common.h"
-
-/* {{{ dio_stream_mode_to_flags
- * Convert an fopen() mode string to open() flags
- */
-static int dio_stream_mode_to_flags(const char *mode) {
-	int  flags = 0, ch = 0, bin = 1;
-
-	switch(mode[ch++]) {
-		case 'r':
-			flags = 0;
-			break;
-		case 'w':
-			flags = O_TRUNC | O_CREAT;
-			break;
-		case 'a':
-			flags = O_APPEND | O_CREAT;
-			break;
-		case 'x':
-			flags = O_EXCL | O_CREAT;
-			break;
-	}
-
-	if (mode[ch] != '+') {
-		bin = (mode[ch++] == 'b');
-	}
-
-	if (mode[ch] == '+') {
-		flags |= O_RDWR;
-	} else if (flags) {
-		flags |= O_WRONLY;
-	} else {
-		flags |= O_RDONLY;
-	}
-
-#if defined(_O_TEXT) && defined(O_BINARY)
-	if (bin) {
-		flags |= O_BINARY;
-	} else {
-		flags |= _O_TEXT;
-	}
-#endif
-
-	return flags;
-}
-/* }}} */
-
-/* {{{ dio_data_rate_to_define
- * Converts a numeric data rate to a termios define
- */
-static int dio_data_rate_to_define(long rate, speed_t *def) {
-	speed_t val;
-
-	switch (rate) {
-		case 0:
-			val = 0;
-			break;
-		case 50:
-			val = B50;
-			break;
-		case 75:
-			val = B75;
-			break;
-		case 110:
-			val = B110;
-			break;
-		case 134:
-			val = B134;
-			break;
-		case 150:
-			val = B150;
-			break;
-		case 200:
-			val = B200;
-			break;
-		case 300:
-			val = B300;
-			break;
-		case 600:
-			val = B600;
-			break;
-		case 1200:
-			val = B1200;
-			break;
-		case 1800:
-			val = B1800;
-			break;
-		case 2400:
-			val = B2400;
-			break;
-		case 4800:
-			val = B4800;
-			break;
-		case 9600:
-			val = B9600;
-			break;
-		case 19200:
-			val = B19200;
-			break;
-		case 38400:
-			val = B38400;
-			break;
-#ifdef B57600
-		case 57600:
-			val = B57600;
-			break;
-#endif
-#ifdef B115200
-		case 115200:
-			val = B115200;
-			break;
-#endif
-#ifdef B230400
-		case 230400:
-			val = B230400;
-			break;
-#endif
-#ifdef B460800
-		case 460800:
-			val = B460800;
-			break;
-#endif
-		default:
-			return 0;
-	}
-
-	*def = val;
-	return 1;
-}
-
-/* {{{ dio_data_bits_to_define
- * Converts a number of data bits to a termios define
- */
-static int dio_data_bits_to_define(int data_bits, int *def) {
-	int val;
-
-	switch (data_bits) {
-		case 8:
-			val = CS8;
-			break;
-		case 7:
-			val = CS7;
-			break;
-		case 6:
-			val = CS6;
-			break;
-		case 5:
-			val = CS5;
-			break;
-		default:
-			return 0;
-	}
-
-	*def = val;
-	return 1;
-}
-/* }}} */
-
-/* {{{ dio_stop_bits_to_define
- * Converts a number of stop bits to a termios define
- */
-static int dio_stop_bits_to_define(int stop_bits, int *def) {
-	int val;
-
-	switch (stop_bits) {
-		case 1:
-			val = 0;
-			break;
-		case 2:
-			val = CSTOPB;
-			break;
-		default:
-			return 0;
-	}
-
-	*def = val;
-	return 1;
-}
-/* }}} */
-
-/* {{{ dio_parity_to_define
- * Converts a parity type to a termios define
- */
-static int dio_parity_to_define(int parity, int *def) {
-	int val;
-
-	switch (parity) {
-		case 0:
-			val = 0;
-			break;
-		case 1:
-			val = PARENB | PARODD;
-			break;
-		case 2:
-			val = PARENB;
-			break;
-		default:
-			return 0;
-	}
-
-	*def = val;
-	return 1;
-}
-/* }}} */
-
-/* {{{ dio_create_stream_data
- * Creates an initialised stream data structure.  Free with efree().
- */
-php_dio_stream_data * dio_create_stream_data(void) {
-	php_dio_posix_stream_data * data = emalloc(sizeof(php_dio_posix_stream_data));
-	dio_init_stream_data(&(data->common));
-	data->fd = -1;
-	data->flags = 0;
-
-	return (php_dio_stream_data *)data;
-}
-/* }}} */
-
-/* {{{ dio_common_write
- * Writes count chars from the buffer to the stream described by the stream data.
- */
-size_t dio_common_write(php_dio_stream_data *data, const char *buf, size_t count) {
-	size_t ret;
-
-	/* Blocking writes can be interrupted by signals etc. If
-	 * interrupted try again. Not sure about non-blocking
-	 * writes but it doesn't hurt to check. */
-	do {
-		ret = write(((php_dio_posix_stream_data*)data)->fd, buf, count);
-		if (ret > 0) {
-			return ret;
-		}
-	} while (errno == EINTR);
-	return 0;
-}
-/* }}} */
-
-#ifdef DIO_NONBLOCK
-/* {{{ dio_timeval_subtract
- * Calculates the difference between two timevals returning the result in the
- * structure pointed to by diffptr.  Returns -1 as error if late time is
- * earlier than early time.
- */
-static int dio_timeval_subtract(struct timeval *late, struct timeval *early, struct timeval *diff) {
-	struct timeval *tmp;
-
-	/* Handle negatives */
-	if (late->tv_sec < early->tv_sec) {
-		return 0;
-	}
-
-	if ((late->tv_sec == early->tv_sec) && (late->tv_usec < early->tv_usec)) {
-		return 0;
-	}
-
-	/* Handle any carry.  If later usec is smaller than earlier usec simple
-	 * subtraction will result in negative value.  Since usec has a maximum
-	 * of one second by adding another second before the subtraction the
-	 * result will always be positive. */
-	if (late->tv_usec < early->tv_usec) {
-		late->tv_usec  += 1000000;
-		late->tv_sec--;
-	}
-
-	/* Once adjusted can just subtract values. */
-	diff->tv_sec  = late->tv_sec  - early->tv_sec;
-	diff->tv_usec = late->tv_usec - early->tv_usec;
-
-	return 1;
-}
-#endif
-
-/* {{{ dio_common_read
- * Reads count chars to the buffer to the stream described by the stream data.
- */
-size_t dio_common_read(php_dio_stream_data *data, const char *buf, size_t count) {
-	int fd = ((php_dio_posix_stream_data*)data)->fd;
-	size_t ret, total = 0;
-	char *ptr = (char*)buf;
-
-	struct timeval timeout, timeouttmp, before, after, diff;
-	fd_set rfds;
-
-	if (!data->has_timeout) {
-		/* Blocking reads can be interrupted by signals etc. If
-		 * interrupted try again. Not sure about non-blocking
-		 * reads but it doesn't hurt to check. */
-		do {
-			ret = read(fd, (char*)ptr, count);
-			if (ret > 0) {
-				return ret;
-			} else if (!ret) {
-				data->end_of_file = 1;
-			}
-		} while ((errno == EINTR) && !data->end_of_file);
-		return 0;
-	}
-#ifdef DIO_NONBLOCK
-	else {
-		/* Clear timed out flag */
-		data->timed_out = 0;
-
-		/* The initial timeout value */
-		timeout.tv_sec  = data->timeout_sec;
-		timeout.tv_usec = data->timeout_usec;
-
-		do {
-			/* The semantics of select() are that you cannot guarantee
-			 * that the timeval structure passed in has not been changed by
-			 * the select call.  So you keep a copy. */
-			timeouttmp = timeout;
-
-			/* The time before we wait for data. */
-			(void) gettimeofday(&before, NULL);
-
-			/* Wait for an event on our file descriptor. */
-			FD_ZERO(&rfds);
-			FD_SET(fd, &rfds);
-
-			ret = select(fd + 1, &rfds, NULL, NULL, &timeouttmp);
-			/* An error. */
-			if ((ret < 0) && (errno != EINTR) && (errno != EAGAIN)) {
-				return 0;
-			}
-
-			/* We have data to read. */
-			if ((ret > 0) && FD_ISSET(fd, &rfds)) {
-				ret = read(fd, ptr, count);
-				/* Another error */
-				if ((ret < 0) && (errno != EINTR) && (errno != EAGAIN)) {
-					return 0;
-				}
-
-				if (ret > 0) {
-					/* Got data, add it to the buffer. */
-					ptr   += ret;
-					total += ret;
-					count -= ret;
-				} else if (!ret) {
-					/* This should never happen since how can we have
-					 * data to read at an end of file, but still
-					 * just in case! */
-					data->end_of_file = 1;
-					break;
-				}
-			}
-
-			/* If not timed out and not end of file and not all data read
-			 * calculate how long it took us and loop if we still have time
-			 * out time left. */
-			if (count) {
-				(void) gettimeofday(&after, NULL);
-
-				/* Diff the timevals */
-				(void) dio_timeval_subtract(&after, &before, &diff);
-
-				/* Now adjust the timeout. */
-				if (!dio_timeval_subtract(&timeout, &diff, &timeout)) {
-					/* If it errors we've run out of time. */
-					data->timed_out = 1;
-					break;
-				} else if (!timeout.tv_sec && !(timeout.tv_usec / 1000)) {
-					/* Check for rounding issues (millisecond accuracy) */
-					data->timed_out = 1;
-					break;
-				}
-			}
-		} while (count); /* Until time out or end of file or all data read. */
-
-		return total;
-	}
-#endif
-}
-/* }}} */
-
-/* {{{ php_dio_stream_data
- * Closes the php_stream.
- */
-int dio_common_close(php_dio_stream_data *data) {
-	if (close(((php_dio_posix_stream_data*)data)->fd) < 0) {
-		return 0;
-	}
-
-	return 1;
-}
-/* }}} */
-
-/* {{{ dio_common_set_option
- * Sets/gets stream options
- */
-int dio_common_set_option(php_dio_stream_data *data, int option, int value, void *ptrparam) {
-	int fd = ((php_dio_posix_stream_data*)data)->fd;
-	int old_is_blocking;
-	int flags;
-
-	switch (option) {
-#ifdef DIO_NONBLOCK
-		case PHP_STREAM_OPTION_READ_TIMEOUT:
-			if (ptrparam) {
-				struct timeval *tv = (struct timeval*)ptrparam;
-
-				flags = fcntl(fd, F_GETFL, 0);
-
-				/* A timeout of zero seconds and zero microseconds disables
-				   any existing timeout. */
-				if (tv->tv_sec || tv->tv_usec) {
-					data->timeout_sec = tv->tv_sec;
-					data->timeout_usec = tv->tv_usec;
-					data->has_timeout = -1;
-					(void) fcntl(fd, F_SETFL, flags & ~DIO_NONBLOCK);
-				} else {
-					data->timeout_sec = 0;
-					data->timeout_usec = 0;
-					data->has_timeout = 0;
-					data->timed_out = 0;
-					(void) fcntl(fd, F_SETFL, flags | DIO_NONBLOCK);
-				}
-
-				return PHP_STREAM_OPTION_RETURN_OK;
-			} else {
-				return PHP_STREAM_OPTION_RETURN_ERR;
-			}
-
-		case PHP_STREAM_OPTION_BLOCKING:
-			flags = fcntl(fd, F_GETFL, 0);
-			if (value) {
-				flags &= ~DIO_NONBLOCK;
-			} else {
-				flags |= DIO_NONBLOCK;
-			}
-			(void) fcntl(fd, F_SETFL, flags);
-
-			old_is_blocking = data->is_blocking;
-			data->is_blocking = value;
-			return old_is_blocking ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
-#endif /* O_NONBLOCK */
-
-		default:
-			break;
-	}
-
-	return 1;
-}
-/* }}} */
-
-/* {{{ dio_raw_open_stream
- * Opens the underlying stream.
- */
-int dio_raw_open_stream(char *filename, char *mode, php_dio_stream_data *data TSRMLS_DC) {
-	php_dio_posix_stream_data *pdata = (php_dio_posix_stream_data*)data;
-	pdata->flags = dio_stream_mode_to_flags(mode);
-
-#ifdef DIO_NONBLOCK
-	if (!data->is_blocking || data->has_timeout) {
-		pdata->flags |= DIO_NONBLOCK;
-	}
-#endif
-
-	/* Open the file and handle any errors. */
-#ifdef DIO_HAS_FILEPERMS
-	if (data->has_perms) {
-		pdata->fd = open(filename, pdata->flags, (mode_t)data->perms);
-	} else {
-		pdata->fd = open(filename, pdata->flags);
-	}
-#else
-	pdata->fd = open(filename, pdata->flags);
-#endif
-
-	if (pdata->fd < 0) {
-		switch (errno) {
-			case EEXIST:
-				php_error_docref(NULL TSRMLS_CC, E_WARNING, "File exists!");
-				return 0;
-			default:
-				return 0;
-		}
-	}
-
-	return 1;
-}
-/* }}} */
-
-/* {{{ dio_serial_init
- * Initialises the serial settings storing the original settings before hand.
- */
-static int dio_serial_init(php_dio_stream_data *data TSRMLS_DC) {
-	php_dio_posix_stream_data *pdata = (php_dio_posix_stream_data*)data;
-	int ret = 0, data_bits_def, stop_bits_def, parity_def;
-	struct termios tio;
-	speed_t rate_def;
-
-	if (!dio_data_rate_to_define(data->data_rate, &rate_def)) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid data_rate value (%ld)", data->data_rate);
-		return 0;
-	}
-
-	if (!dio_data_bits_to_define(data->data_bits, &data_bits_def)) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid data_bits value (%d)", data->data_bits);
-		return 0;
-	}
-
-	if (!dio_stop_bits_to_define(data->stop_bits, &stop_bits_def)) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid stop_bits value (%d)", data->stop_bits);
-		return 0;
-	}
-
-	if (!dio_parity_to_define(data->parity, &parity_def)) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid parity value (%d)", data->parity);
-		return 0;
-	}
-
-	ret = tcgetattr(pdata->fd, &(pdata->oldtio));
-	if (ret < 0) {
-		if ((errno == ENOTTY) || (errno == ENODEV)) {
-			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not a serial port or terminal!");
-		}
-		return 0;
-	}
-
-	ret = tcgetattr(pdata->fd, &tio);
-	if (ret < 0) {
-		return 0;
-	}
-
-	if (data->canonical) {
-		tio.c_iflag = IGNPAR | ICRNL;
-		tio.c_oflag = 0;
-		tio.c_lflag = ICANON;
-	} else {
-		cfmakeraw(&tio);
-	}
-
-	cfsetispeed(&tio, rate_def);
-	cfsetospeed(&tio, rate_def);
-
-	tio.c_cflag &= ~CSIZE;
-	tio.c_cflag |= data_bits_def;
-	tio.c_cflag &= ~CSTOPB;
-	tio.c_cflag |= stop_bits_def;
-	tio.c_cflag &= ~(PARENB|PARODD);
-	tio.c_cflag |= parity_def;
-
-#ifdef CRTSCTS
-	tio.c_cflag &= ~(CLOCAL | CRTSCTS);
-#else
-	tio.c_cflag &= ~CLOCAL;
-#endif
-	if (!data->flow_control) {
-		tio.c_cflag |= CLOCAL;
-#ifdef CRTSCTS
-	} else {
-		tio.c_cflag |= CRTSCTS;
-#endif
-	}
-
-	ret = tcsetattr(pdata->fd, TCSANOW, &tio);
-	if (ret < 0) {
-		return 0;
-	}
-
-	return 1;
-}
-/* }}} */
-
-/* {{{ dio_serial_uninit
- * Restores the serial settings back to their original state.
- */
-int dio_serial_uninit(php_dio_stream_data *data) {
-	php_dio_posix_stream_data *pdata = (php_dio_posix_stream_data*)data;
-	int ret;
-
-	do {
-		ret = tcsetattr(pdata->fd, TCSANOW, &(pdata->oldtio));
-	} while ((ret < 0) && (errno == EINTR));
-
-	return 1;
-}
-/* }}} */
-
-/* {{{ dio_serial_flush
- * Purges the serial buffers of data.
- */
-int dio_serial_purge(php_dio_stream_data *data) {
-	php_dio_posix_stream_data *pdata = (php_dio_posix_stream_data*)data;
-	int ret;
-
-	if ((pdata->flags & O_RDWR) == O_RDWR) {
-		ret = tcflush(pdata->fd, TCIOFLUSH);
-	} else if ((pdata->flags & O_WRONLY) == O_WRONLY) {
-		ret = tcflush(pdata->fd, TCOFLUSH);
-	} else if ((pdata->flags & O_RDONLY) == O_RDONLY) {
-		ret = tcflush(pdata->fd, TCIFLUSH);
-	}
-
-	if (ret < 0) {
-		return 0;
-	}
-
-	return 1;
-}
-/* }}} */
-
-/* {{{ dio_serial_open_stream
- * Opens the underlying stream.
- */
-int dio_serial_open_stream(char *filename, char *mode, php_dio_stream_data *data TSRMLS_DC) {
-	php_dio_posix_stream_data *pdata = (php_dio_posix_stream_data*)data;
-
-#ifdef O_NOCTTY
-	/* We don't want a controlling TTY */
-	pdata->flags |= O_NOCTTY;
-#endif
-
-	if (!dio_raw_open_stream(filename, mode, data TSRMLS_CC)) {
-		return 0;
-	}
-
-	if (!dio_serial_init(data TSRMLS_CC)) {
-		close(pdata->fd);
-		return 0;
-	}
-
-	return 1;
-}
-/* }}} */
-
-/*
- * Local variables:
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim600: fdm=marker
- * vim: sw=4 ts=4 noet
- */
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 5                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 2009 Melanie Rhianna Lewis                             |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.0 of the PHP license,       |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_0.txt.                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Author: Melanie Rhianna Lewis <cyberspice@php.net>                   |
+   +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+
+#include "php_dio_common.h"
+
+/* {{{ dio_stream_mode_to_flags
+ * Convert an fopen() mode string to open() flags
+ */
+static int dio_stream_mode_to_flags(const char *mode) {
+	int  flags = 0, ch = 0, bin = 1;
+
+	switch(mode[ch++]) {
+		case 'r':
+			flags = 0;
+			break;
+		case 'w':
+			flags = O_TRUNC | O_CREAT;
+			break;
+		case 'a':
+			flags = O_APPEND | O_CREAT;
+			break;
+		case 'x':
+			flags = O_EXCL | O_CREAT;
+			break;
+	}
+
+	if (mode[ch] != '+') {
+		bin = (mode[ch++] == 'b');
+	}
+
+	if (mode[ch] == '+') {
+		flags |= O_RDWR;
+	} else if (flags) {
+		flags |= O_WRONLY;
+	} else {
+		flags |= O_RDONLY;
+	}
+
+#if defined(_O_TEXT) && defined(O_BINARY)
+	if (bin) {
+		flags |= O_BINARY;
+	} else {
+		flags |= _O_TEXT;
+	}
+#endif
+
+	return flags;
+}
+/* }}} */
+
+/* {{{ dio_data_rate_to_define
+ * Converts a numeric data rate to a termios define
+ */
+static int dio_data_rate_to_define(long rate, speed_t *def) {
+	speed_t val;
+
+	switch (rate) {
+		case 0:
+			val = 0;
+			break;
+		case 50:
+			val = B50;
+			break;
+		case 75:
+			val = B75;
+			break;
+		case 110:
+			val = B110;
+			break;
+		case 134:
+			val = B134;
+			break;
+		case 150:
+			val = B150;
+			break;
+		case 200:
+			val = B200;
+			break;
+		case 300:
+			val = B300;
+			break;
+		case 600:
+			val = B600;
+			break;
+		case 1200:
+			val = B1200;
+			break;
+		case 1800:
+			val = B1800;
+			break;
+		case 2400:
+			val = B2400;
+			break;
+		case 4800:
+			val = B4800;
+			break;
+		case 9600:
+			val = B9600;
+			break;
+		case 19200:
+			val = B19200;
+			break;
+		case 38400:
+			val = B38400;
+			break;
+#ifdef B57600
+		case 57600:
+			val = B57600;
+			break;
+#endif
+#ifdef B115200
+		case 115200:
+			val = B115200;
+			break;
+#endif
+#ifdef B230400
+		case 230400:
+			val = B230400;
+			break;
+#endif
+#ifdef B460800
+		case 460800:
+			val = B460800;
+			break;
+#endif
+		default:
+			return 0;
+	}
+
+	*def = val;
+	return 1;
+}
+
+/* {{{ dio_data_bits_to_define
+ * Converts a number of data bits to a termios define
+ */
+static int dio_data_bits_to_define(int data_bits, int *def) {
+	int val;
+
+	switch (data_bits) {
+		case 8:
+			val = CS8;
+			break;
+		case 7:
+			val = CS7;
+			break;
+		case 6:
+			val = CS6;
+			break;
+		case 5:
+			val = CS5;
+			break;
+		default:
+			return 0;
+	}
+
+	*def = val;
+	return 1;
+}
+/* }}} */
+
+/* {{{ dio_stop_bits_to_define
+ * Converts a number of stop bits to a termios define
+ */
+static int dio_stop_bits_to_define(int stop_bits, int *def) {
+	int val;
+
+	switch (stop_bits) {
+		case 1:
+			val = 0;
+			break;
+		case 2:
+			val = CSTOPB;
+			break;
+		default:
+			return 0;
+	}
+
+	*def = val;
+	return 1;
+}
+/* }}} */
+
+/* {{{ dio_parity_to_define
+ * Converts a parity type to a termios define
+ */
+static int dio_parity_to_define(int parity, int *def) {
+	int val;
+
+	switch (parity) {
+		case 0:
+			val = 0;
+			break;
+		case 1:
+			val = PARENB | PARODD;
+			break;
+		case 2:
+			val = PARENB;
+			break;
+		default:
+			return 0;
+	}
+
+	*def = val;
+	return 1;
+}
+/* }}} */
+
+/* {{{ dio_create_stream_data
+ * Creates an initialised stream data structure.  Free with efree().
+ */
+php_dio_stream_data * dio_create_stream_data(void) {
+	php_dio_posix_stream_data * data = emalloc(sizeof(php_dio_posix_stream_data));
+	dio_init_stream_data(&(data->common));
+	data->fd = -1;
+	data->flags = 0;
+
+	return (php_dio_stream_data *)data;
+}
+/* }}} */
+
+/* {{{ dio_common_write
+ * Writes count chars from the buffer to the stream described by the stream data.
+ */
+size_t dio_common_write(php_dio_stream_data *data, const char *buf, size_t count) {
+	size_t ret;
+
+	/* Blocking writes can be interrupted by signals etc. If
+	 * interrupted try again. Not sure about non-blocking
+	 * writes but it doesn't hurt to check. */
+	do {
+		ret = write(((php_dio_posix_stream_data*)data)->fd, buf, count);
+		if (ret > 0) {
+			return ret;
+		}
+	} while (errno == EINTR);
+	return 0;
+}
+/* }}} */
+
+#ifdef DIO_NONBLOCK
+/* {{{ dio_timeval_subtract
+ * Calculates the difference between two timevals returning the result in the
+ * structure pointed to by diffptr.  Returns -1 as error if late time is
+ * earlier than early time.
+ */
+static int dio_timeval_subtract(struct timeval *late, struct timeval *early, struct timeval *diff) {
+	struct timeval *tmp;
+
+	/* Handle negatives */
+	if (late->tv_sec < early->tv_sec) {
+		return 0;
+	}
+
+	if ((late->tv_sec == early->tv_sec) && (late->tv_usec < early->tv_usec)) {
+		return 0;
+	}
+
+	/* Handle any carry.  If later usec is smaller than earlier usec simple
+	 * subtraction will result in negative value.  Since usec has a maximum
+	 * of one second by adding another second before the subtraction the
+	 * result will always be positive. */
+	if (late->tv_usec < early->tv_usec) {
+		late->tv_usec  += 1000000;
+		late->tv_sec--;
+	}
+
+	/* Once adjusted can just subtract values. */
+	diff->tv_sec  = late->tv_sec  - early->tv_sec;
+	diff->tv_usec = late->tv_usec - early->tv_usec;
+
+	return 1;
+}
+#endif
+
+/* {{{ dio_common_read
+ * Reads count chars to the buffer to the stream described by the stream data.
+ */
+size_t dio_common_read(php_dio_stream_data *data, const char *buf, size_t count) {
+	int fd = ((php_dio_posix_stream_data*)data)->fd;
+	size_t ret, total = 0;
+	char *ptr = (char*)buf;
+
+	struct timeval timeout, timeouttmp, before, after, diff;
+	fd_set rfds;
+
+	if (!data->has_timeout) {
+		/* Blocking reads can be interrupted by signals etc. If
+		 * interrupted try again. Not sure about non-blocking
+		 * reads but it doesn't hurt to check. */
+		do {
+			ret = read(fd, (char*)ptr, count);
+			if (ret > 0) {
+				return ret;
+			} else if (!ret) {
+				data->end_of_file = 1;
+			}
+		} while ((errno == EINTR) && !data->end_of_file);
+		return 0;
+	}
+#ifdef DIO_NONBLOCK
+	else {
+		/* Clear timed out flag */
+		data->timed_out = 0;
+
+		/* The initial timeout value */
+		timeout.tv_sec  = data->timeout_sec;
+		timeout.tv_usec = data->timeout_usec;
+
+		do {
+			/* The semantics of select() are that you cannot guarantee
+			 * that the timeval structure passed in has not been changed by
+			 * the select call.  So you keep a copy. */
+			timeouttmp = timeout;
+
+			/* The time before we wait for data. */
+			(void) gettimeofday(&before, NULL);
+
+			/* Wait for an event on our file descriptor. */
+			FD_ZERO(&rfds);
+			FD_SET(fd, &rfds);
+
+			ret = select(fd + 1, &rfds, NULL, NULL, &timeouttmp);
+			/* An error. */
+			if ((ret < 0) && (errno != EINTR) && (errno != EAGAIN)) {
+				return 0;
+			}
+
+			/* We have data to read. */
+			if ((ret > 0) && FD_ISSET(fd, &rfds)) {
+				ret = read(fd, ptr, count);
+				/* Another error */
+				if ((ret < 0) && (errno != EINTR) && (errno != EAGAIN)) {
+					return 0;
+				}
+
+				if (ret > 0) {
+					/* Got data, add it to the buffer. */
+					ptr   += ret;
+					total += ret;
+					count -= ret;
+				} else if (!ret) {
+					/* This should never happen since how can we have
+					 * data to read at an end of file, but still
+					 * just in case! */
+					data->end_of_file = 1;
+					break;
+				}
+			}
+
+			/* If not timed out and not end of file and not all data read
+			 * calculate how long it took us and loop if we still have time
+			 * out time left. */
+			if (count) {
+				(void) gettimeofday(&after, NULL);
+
+				/* Diff the timevals */
+				(void) dio_timeval_subtract(&after, &before, &diff);
+
+				/* Now adjust the timeout. */
+				if (!dio_timeval_subtract(&timeout, &diff, &timeout)) {
+					/* If it errors we've run out of time. */
+					data->timed_out = 1;
+					break;
+				} else if (!timeout.tv_sec && !(timeout.tv_usec / 1000)) {
+					/* Check for rounding issues (millisecond accuracy) */
+					data->timed_out = 1;
+					break;
+				}
+			}
+		} while (count); /* Until time out or end of file or all data read. */
+
+		return total;
+	}
+#endif
+}
+/* }}} */
+
+/* {{{ php_dio_stream_data
+ * Closes the php_stream.
+ */
+int dio_common_close(php_dio_stream_data *data) {
+	if (close(((php_dio_posix_stream_data*)data)->fd) < 0) {
+		return 0;
+	}
+
+	return 1;
+}
+/* }}} */
+
+/* {{{ dio_common_set_option
+ * Sets/gets stream options
+ */
+int dio_common_set_option(php_dio_stream_data *data, int option, int value, void *ptrparam) {
+	int fd = ((php_dio_posix_stream_data*)data)->fd;
+	int old_is_blocking;
+	int flags;
+
+	switch (option) {
+#ifdef DIO_NONBLOCK
+		case PHP_STREAM_OPTION_READ_TIMEOUT:
+			if (ptrparam) {
+				struct timeval *tv = (struct timeval*)ptrparam;
+
+				flags = fcntl(fd, F_GETFL, 0);
+
+				/* A timeout of zero seconds and zero microseconds disables
+				   any existing timeout. */
+				if (tv->tv_sec || tv->tv_usec) {
+					data->timeout_sec = tv->tv_sec;
+					data->timeout_usec = tv->tv_usec;
+					data->has_timeout = -1;
+					(void) fcntl(fd, F_SETFL, flags & ~DIO_NONBLOCK);
+				} else {
+					data->timeout_sec = 0;
+					data->timeout_usec = 0;
+					data->has_timeout = 0;
+					data->timed_out = 0;
+					(void) fcntl(fd, F_SETFL, flags | DIO_NONBLOCK);
+				}
+
+				return PHP_STREAM_OPTION_RETURN_OK;
+			} else {
+				return PHP_STREAM_OPTION_RETURN_ERR;
+			}
+
+		case PHP_STREAM_OPTION_BLOCKING:
+			flags = fcntl(fd, F_GETFL, 0);
+			if (value) {
+				flags &= ~DIO_NONBLOCK;
+			} else {
+				flags |= DIO_NONBLOCK;
+			}
+			(void) fcntl(fd, F_SETFL, flags);
+
+			old_is_blocking = data->is_blocking;
+			data->is_blocking = value;
+			return old_is_blocking ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
+#endif /* O_NONBLOCK */
+
+		default:
+			break;
+	}
+
+	return 1;
+}
+/* }}} */
+
+/* {{{ dio_raw_open_stream
+ * Opens the underlying stream.
+ */
+int dio_raw_open_stream(char *filename, char *mode, php_dio_stream_data *data TSRMLS_DC) {
+	php_dio_posix_stream_data *pdata = (php_dio_posix_stream_data*)data;
+	pdata->flags = dio_stream_mode_to_flags(mode);
+
+#ifdef DIO_NONBLOCK
+	if (!data->is_blocking || data->has_timeout) {
+		pdata->flags |= DIO_NONBLOCK;
+	}
+#endif
+
+	/* Open the file and handle any errors. */
+#ifdef DIO_HAS_FILEPERMS
+	if (data->has_perms) {
+		pdata->fd = open(filename, pdata->flags, (mode_t)data->perms);
+	} else {
+		pdata->fd = open(filename, pdata->flags);
+	}
+#else
+	pdata->fd = open(filename, pdata->flags);
+#endif
+
+	if (pdata->fd < 0) {
+		switch (errno) {
+			case EEXIST:
+				php_error_docref(NULL TSRMLS_CC, E_WARNING, "File exists!");
+				return 0;
+			default:
+				return 0;
+		}
+	}
+
+	return 1;
+}
+/* }}} */
+
+/* {{{ dio_serial_init
+ * Initialises the serial settings storing the original settings before hand.
+ */
+static int dio_serial_init(php_dio_stream_data *data TSRMLS_DC) {
+	php_dio_posix_stream_data *pdata = (php_dio_posix_stream_data*)data;
+	int ret = 0, data_bits_def, stop_bits_def, parity_def;
+	struct termios tio;
+	speed_t rate_def;
+
+	if (!dio_data_rate_to_define(data->data_rate, &rate_def)) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid data_rate value (%ld)", data->data_rate);
+		return 0;
+	}
+
+	if (!dio_data_bits_to_define(data->data_bits, &data_bits_def)) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid data_bits value (%d)", data->data_bits);
+		return 0;
+	}
+
+	if (!dio_stop_bits_to_define(data->stop_bits, &stop_bits_def)) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid stop_bits value (%d)", data->stop_bits);
+		return 0;
+	}
+
+	if (!dio_parity_to_define(data->parity, &parity_def)) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid parity value (%d)", data->parity);
+		return 0;
+	}
+
+	ret = tcgetattr(pdata->fd, &(pdata->oldtio));
+	if (ret < 0) {
+		if ((errno == ENOTTY) || (errno == ENODEV)) {
+			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not a serial port or terminal!");
+		}
+		return 0;
+	}
+
+	ret = tcgetattr(pdata->fd, &tio);
+	if (ret < 0) {
+		return 0;
+	}
+
+	if (data->canonical) {
+		tio.c_iflag = IGNPAR | ICRNL;
+		tio.c_oflag = 0;
+		tio.c_lflag = ICANON;
+	} else {
+		cfmakeraw(&tio);
+	}
+
+	cfsetispeed(&tio, rate_def);
+	cfsetospeed(&tio, rate_def);
+
+	tio.c_cflag &= ~CSIZE;
+	tio.c_cflag |= data_bits_def;
+	tio.c_cflag &= ~CSTOPB;
+	tio.c_cflag |= stop_bits_def;
+	tio.c_cflag &= ~(PARENB|PARODD);
+	tio.c_cflag |= parity_def;
+
+#ifdef CRTSCTS
+	tio.c_cflag &= ~(CLOCAL | CRTSCTS);
+#else
+	tio.c_cflag &= ~CLOCAL;
+#endif
+	if (!data->flow_control) {
+		tio.c_cflag |= CLOCAL;
+#ifdef CRTSCTS
+	} else {
+		tio.c_cflag |= CRTSCTS;
+#endif
+	}
+
+	ret = tcsetattr(pdata->fd, TCSANOW, &tio);
+	if (ret < 0) {
+		return 0;
+	}
+
+	return 1;
+}
+/* }}} */
+
+/* {{{ dio_serial_uninit
+ * Restores the serial settings back to their original state.
+ */
+int dio_serial_uninit(php_dio_stream_data *data) {
+	php_dio_posix_stream_data *pdata = (php_dio_posix_stream_data*)data;
+	int ret;
+
+	do {
+		ret = tcsetattr(pdata->fd, TCSANOW, &(pdata->oldtio));
+	} while ((ret < 0) && (errno == EINTR));
+
+	return 1;
+}
+/* }}} */
+
+/* {{{ dio_serial_flush
+ * Purges the serial buffers of data.
+ */
+int dio_serial_purge(php_dio_stream_data *data) {
+	php_dio_posix_stream_data *pdata = (php_dio_posix_stream_data*)data;
+	int ret;
+
+	if ((pdata->flags & O_RDWR) == O_RDWR) {
+		ret = tcflush(pdata->fd, TCIOFLUSH);
+	} else if ((pdata->flags & O_WRONLY) == O_WRONLY) {
+		ret = tcflush(pdata->fd, TCOFLUSH);
+	} else if ((pdata->flags & O_RDONLY) == O_RDONLY) {
+		ret = tcflush(pdata->fd, TCIFLUSH);
+	}
+
+	if (ret < 0) {
+		return 0;
+	}
+
+	return 1;
+}
+/* }}} */
+
+/* {{{ dio_serial_open_stream
+ * Opens the underlying stream.
+ */
+int dio_serial_open_stream(char *filename, char *mode, php_dio_stream_data *data TSRMLS_DC) {
+	php_dio_posix_stream_data *pdata = (php_dio_posix_stream_data*)data;
+
+#ifdef O_NOCTTY
+	/* We don't want a controlling TTY */
+	pdata->flags |= O_NOCTTY;
+#endif
+
+	if (!dio_raw_open_stream(filename, mode, data TSRMLS_CC)) {
+		return 0;
+	}
+
+	if (!dio_serial_init(data TSRMLS_CC)) {
+		close(pdata->fd);
+		return 0;
+	}
+
+	return 1;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim600: fdm=marker
+ * vim: sw=4 ts=4 noet
+ */
--- a/dio_win32.c
+++ b/dio_win32.c
@@ -1,759 +1,759 @@
-/*
-   +----------------------------------------------------------------------+
-   | PHP Version 5                                                        |
-   +----------------------------------------------------------------------+
-   | Copyright (c) 2009 Melanie Rhianna Lewis                             |
-   +----------------------------------------------------------------------+
-   | This source file is subject to version 3.0 of the PHP license,       |
-   | that is bundled with this package in the file LICENSE, and is        |
-   | available through the world-wide-web at the following url:           |
-   | http://www.php.net/license/3_0.txt.                                  |
-   | If you did not receive a copy of the PHP license and are unable to   |
-   | obtain it through the world-wide-web, please send a note to          |
-   | license@php.net so we can mail you a copy immediately.               |
-   +----------------------------------------------------------------------+
-   | Author: Melanie Rhianna Lewis <cyberspice@php.net>                   |
-   +----------------------------------------------------------------------+
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "php.h"
-#include "php_dio_common.h"
-
-#ifndef ZEND_WIN32
-#error ZEND_WIN32 not defined!
-#endif
-
-/* {{{ dio_last_error_php_error
- * Generates a PHP error message based upon the last Windows error.
- */
-static void dio_last_error_php_error(int level, char * message TSRMLS_DC) {
-	LPVOID msgbuf;
-	DWORD  msgbuflen;
-	char * errmsg;
-	DWORD  err;
-
-#ifdef UNICODE
-	DWORD  errmsglen;
-#endif
-
-	err = GetLastError();
-	msgbuflen = FormatMessage(
-		FORMAT_MESSAGE_ALLOCATE_BUFFER|
-		FORMAT_MESSAGE_FROM_SYSTEM|
-		FORMAT_MESSAGE_IGNORE_INSERTS,
-		NULL,
-		err,
-		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-		(LPTSTR)&msgbuf,
-		0,
-		NULL);
-
-#ifdef UNICODE
-
-	/* Get the length of the converted message */
-	errmsglen = WideCharToMultibyte(
-		CP_ACP,
-		0
-		(LPCWSTR)msgbuf,
-		-1,
-		(LPSTR)errmsg,
-		0,
-		NULL,
-		NULL);
-
-	/* Allocate a buffer */
-	errmsg = emalloc(errmsglen);
-	if (!errmsg) {
-		php_error_docref(NULL TSRMLS_CC, E_ERROR, "Out of memory in dio_last_error_php_error()!");
-		LocalFree(msgbuf);
-		return;
-	}
-
-	/* Convert the message */
-	errmsglen = WideCharToMultibyte(
-		CP_ACP,
-		0
-		(LPCWSTR)msgbuf,
-		-1,
-		(LPSTR)errmsg,
-		errmsglen,
-		NULL,
-		NULL);
-
-#else
-	errmsg = (char *)msgbuf;
-#endif
-
-	php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s[ERROR %d] %s", message, err, errmsg);
-
-	LocalFree(msgbuf);
-#ifdef UNICODE
-	efree(errmsg);
-#endif
-}
-
-/* {{{ dio_data_rate_to_define
- * Converts a numeric data rate to a termios define
- */
-static int dio_data_rate_to_define(long rate, DWORD *def) {
-	switch (rate) {
-		case 75:
-		case 110:
-		case 134:
-		case 150:
-		case 300:
-		case 600:
-		case 1200:
-		case 1800:
-		case 2400:
-		case 4800:
-		case 7200:
-		case 9600:
-		case 14400:
-		case 19200:
-		case 38400:
-		case 57600:
-		case 115200:
-		case 56000:
-		case 128000:
-		case 256000:
-			break;
-		default:
-			return 0;
-	}
-
-	*def = (DWORD)rate;
-	return 1;
-}
-/* }}} */
-
-
-/* {{{ dio_data_bits_to_define
- * Converts a number of data bits to a termios define
- */
-static int dio_data_bits_to_define(int data_bits, DWORD *def) {
-	switch (data_bits) {
-		case 8:
-		case 7:
-		case 6:
-		case 5:
-		case 4:
-			break;
-		default:
-			return 0;
-	}
-
-	*def = (DWORD)data_bits;
-	return 1;
-}
-/* }}} */
-
-/* {{{ dio_stop_bits_to_define
- * Converts a number of stop bits to a termios define
- */
-static int dio_stop_bits_to_define(int stop_bits, DWORD *def) {
-	DWORD val;
-
-	switch (stop_bits) {
-		case 1:
-			val = ONESTOPBIT;
-			break;
-		case 2:
-			val = TWOSTOPBITS;
-			break;
-		case 3:
-			val = ONE5STOPBITS;
-			break;
-		default:
-			return 0;
-	}
-
-	*def = val;
-	return 1;
-}
-/* }}} */
-
-/* {{{ dio_parity_to_define
- * Converts a parity type to a termios define
- */
-static int dio_parity_to_define(int parity, DWORD *def) {
-	DWORD val;
-
-	switch (parity) {
-		case 0:
-			val = NOPARITY;
-			break;
-		case 1:
-			val = ODDPARITY;
-			break;
-		case 2:
-			val = EVENPARITY;
-			break;
-		default:
-			return 0;
-	}
-
-	*def = val;
-	return 1;
-}
-/* }}} */
-
-/* {{{ dio_create_stream_data
- * Creates an initialised stream data structure.  Free with efree().
- */
-php_dio_stream_data * dio_create_stream_data(void) {
-	php_dio_win32_stream_data * data = emalloc(sizeof(php_dio_win32_stream_data));
-	memset(data, 0, sizeof(php_dio_win32_stream_data));
-	dio_init_stream_data(&(data->common));
-	data->handle = INVALID_HANDLE_VALUE;
-	data->desired_access = 0;
-	data->creation_disposition = 0;
-	data->olddcb.DCBlength = sizeof(DCB);
-
-	return (php_dio_stream_data *)data;
-}
-/* }}} */
-
-/* {{{ dio_common_write
- * Writes count chars from the buffer to the stream described by the stream data.
- */
-size_t dio_common_write(php_dio_stream_data *data, const char *buf, size_t count) {
-	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
-	DWORD total = 0;
-
-	if (WriteFile(wdata->handle, buf, (DWORD)count, &total, NULL)) {
-		return (size_t)total;
-	}
-
-	return 0;
-}
-/* }}} */
-
-/* {{{ dio_buffer_read
- * Reads any available chars from the canonical buffer.
- */
-static size_t dio_buffer_read(php_dio_win32_stream_data *wdata, const char *buf, size_t count) {
-	php_dio_win32_canon_data *canon_data = wdata->canon_data;
-	size_t total = 0;
-
-	/* Read always follows write.  I.e. if read ptr > write ptr buffer has
-	   wrapped and so we need to copy two blocks of data. */
-	if (canon_data->read_pos > canon_data->write_pos) {
-
-		/* Check we actually need to copy both blocks */
-		if ((canon_data->size - canon_data->read_pos) > count) {
-
-			/* No we don't.  Just copy as much as we were asked for. */
-			memcpy((char*)buf, 
-				   &(canon_data->buf[canon_data->read_pos]), 
-				   count);
-			/* Update the read pointer. */
-			canon_data->read_pos += count;
-
-			/* Return the amount read. */
-			return count;
-		} else {
-
-			/* We need to copy both blocks so copy data up to the end of 
-			   the buffer. */
-			total = canon_data->size - canon_data->read_pos;
-			memcpy((char*)buf, 
-				   &(canon_data->buf[canon_data->read_pos]), 
-				   total);
-			canon_data->read_pos = 0;
-			count -= total;
-
-			/* Now copy the data from the start of the buffer either up
-			   count or the number of bytes in the buffer. */
-
-			if (canon_data->write_pos > count) {
-				memcpy((char*)buf, canon_data->buf, count);
-				canon_data->read_pos = count;
-				total += count;
-
-				return total;
-			} else {
-				memcpy((char*)buf, canon_data->buf, canon_data->write_pos);
-				canon_data->read_pos = canon_data->write_pos;
-				total += canon_data->write_pos;
-
-				return total;
-			}
-		}
-
-	/* Else if write follows read.  This is a simpler case.  We just copy 
-	   either all the data buffered or count, which ever is smaller. */
-	} else if (canon_data->write_pos > canon_data->read_pos) {
-		if ((canon_data->write_pos - canon_data->read_pos) > count) {
-			memcpy((char*)buf, 
-				   &(canon_data->buf[canon_data->read_pos]), 
-				   count);
-			canon_data->read_pos += count;
-
-			return count;
-		} else {
-			total = canon_data->write_pos - canon_data->read_pos;
-			memcpy((char*)buf, 
-				   &(canon_data->buf[canon_data->read_pos]), 
-				   total);
-			canon_data->read_pos += total;
-
-			return total;
-		}
-	}
-
-	/* Else we need to read more data from the data port. */
-	return 0;
-}
-
-/* {{{ dio_com_read
- * Read chars from the data port.
- */
-static size_t dio_com_read(php_dio_stream_data *data, const char *buf, size_t count) {
-	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
-	DWORD err, total = 0;
-
-	if (ReadFile(wdata->handle, (void*)buf, (DWORD)count, &total, NULL)) {
-
-		if (total) {
-			return (size_t)total;
-		}
-
-		data->end_of_file = 1;
-	}
-
-	if (!data->end_of_file) {
-		err = GetLastError();
-
-		if (ERROR_HANDLE_EOF == err) {
-			data->end_of_file = 1;
-		}
-	}
-
-	return 0;
-}
-
-/* {{{ dio_canonical_read
- * Reads chars from the input stream until the internal buffer is full or a new
- * line is reached.
- */
-static size_t dio_canonical_read(php_dio_win32_stream_data *wdata, const char *buf, size_t count) {
-	php_dio_win32_canon_data *canon_data = wdata->canon_data;
-	size_t total = 0;
-	char ch;
-
-	/* See if there's any buffered data and copy it. */
-	total = dio_buffer_read(wdata, buf, count);
-	if (total) {
-		return total;
-	}
-
-	/* Need to read more data from the data port.  Buffer should be empty(er)
-	   by now. */
-	do {
-		/* Is the buffer full? */
-		if (((canon_data->write_pos + 1) % canon_data->size) == 
-			canon_data->read_pos) {
-			break;
-		}
-
-		/* Read a byte from the input checking for EOF. */
-		if (!dio_com_read((php_dio_stream_data*)wdata, &ch, 1)) {
-			break;
-		}
-
-		/* Translate CR to newlines (same as ICRNL in POSIX) */
-		ch = (ch != '\r') ? ch : '\n';
-
-		/* We read a character!  So buffer it. */
-		canon_data->buf[canon_data->write_pos++] = ch;
-		if (canon_data->write_pos >= canon_data->size) {
-			canon_data->write_pos = 0;
-		}
-
-		/* End of line/input (^D)? */
-	} while ((ch != '\n') && (ch != 0x04));
-
-	return dio_buffer_read(wdata, buf, count);
-}
-/* }}} */
-
-/* {{{ dio_common_read
- * Reads count chars to the buffer to the stream described by the stream data.
- */
-size_t dio_common_read(php_dio_stream_data *data, const char *buf, size_t count) {
-
-	/* You ask for no bytes you'll get none :-) */
-	if (!count) {
-		return 0;
-	}
-
-	if (data->canonical) {
-		return dio_canonical_read((php_dio_win32_stream_data*)data, buf, count);
-	} else {
-		return dio_com_read(data, buf, count);
-	}
-}
-/* }}} */
-
-/* {{{ php_dio_stream_data
- * Closes the php_stream.
- */
-int dio_common_close(php_dio_stream_data *data) {
-	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
-
-	if (data->canonical) {
-		efree(wdata->canon_data);
-	}
-
-	if (!CloseHandle(wdata->handle)) {
-		return 0;
-	}
-
-	return 1;
-}
-/* }}} */
-
-/* {{{ dio_common_set_option
- * Sets/gets stream options
- */
-int dio_common_set_option(php_dio_stream_data *data, int option, int value, void *ptrparam) {
-	COMMTIMEOUTS cto = { 0, 0, 0, 0, 0 };
-	int old_is_blocking = 0;
-
-	/* Can't do timeouts or non blocking with raw windows streams :-( */
-	if (DIO_STREAM_TYPE_SERIAL == data->stream_type) {
-		switch (option) {
-			case PHP_STREAM_OPTION_BLOCKING:
-				old_is_blocking   = data->is_blocking;
-				data->is_blocking = value ? 1 : 0;
-
-				/* Only change values if we need to change them. */
-				if (data->is_blocking != old_is_blocking) {
-					/* If we're not blocking but don't have a timeout
-					   set to return immediately */
-					if (!data->is_blocking && !data->has_timeout) {
-						cto.ReadIntervalTimeout = MAXDWORD;
-					}
-
-					/* If we have a timeout ignore the blocking and set
-					   the total time in which to read the data */
-					if (data->has_timeout) {
-						cto.ReadIntervalTimeout = MAXDWORD;
-						cto.ReadTotalTimeoutMultiplier  = MAXDWORD;
-						cto.ReadTotalTimeoutConstant = (data->timeout_usec / 1000) +
-							(data->timeout_sec * 1000);
-					}
-
-					if (!SetCommTimeouts(((php_dio_win32_stream_data*)data)->handle, &cto)) {
-						return PHP_STREAM_OPTION_RETURN_ERR;
-					}
-				}
-				return old_is_blocking ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
-
-			case PHP_STREAM_OPTION_READ_TIMEOUT:
-				if (ptrparam) {
-					/* struct timeval is supported with PHP_WIN32 defined. */
-					struct timeval *tv = (struct timeval*)ptrparam;
-
-					/* A timeout of zero seconds and zero microseconds disables
-					   any existing timeout. */
-					if (tv->tv_sec || tv->tv_usec) {
-						data->timeout_sec = tv->tv_sec;
-						data->timeout_usec = tv->tv_usec;
-						data->has_timeout = -1;
-
-						cto.ReadIntervalTimeout = MAXDWORD;
-						cto.ReadTotalTimeoutMultiplier  = MAXDWORD;
-						cto.ReadTotalTimeoutConstant = (data->timeout_usec / 1000) +
-							(data->timeout_sec * 1000);
-					} else {
-						data->timeout_sec = 0;
-						data->timeout_usec = 0;
-						data->has_timeout = 0;
-						data->timed_out = 0;
-
-						/* If we're not blocking but don't have a timeout
-						   set to return immediately */
-						if (!data->is_blocking) {
-							cto.ReadIntervalTimeout = MAXDWORD;
-						}
-					}
-
-					if (!SetCommTimeouts(((php_dio_win32_stream_data*)data)->handle, &cto)) {
-						return PHP_STREAM_OPTION_RETURN_ERR;
-					} else {
-						return PHP_STREAM_OPTION_RETURN_OK;
-					}
-				} else {
-					return PHP_STREAM_OPTION_RETURN_ERR;
-				}
-
-			default:
-				break;
-		}
-	}
-
-	return 1;
-}
-/* }}} */
-
-/* {{{ dio_raw_open_stream
- * Opens the underlying stream.
- */
-int dio_raw_open_stream(char *filename, char *mode, php_dio_stream_data *data TSRMLS_DC) {
-	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
-	DWORD err;
-
-	switch(*mode) {
-		case 'r':
-			wdata->creation_disposition = OPEN_EXISTING;
-			break;
-		case 'w':
-			wdata->creation_disposition = TRUNCATE_EXISTING;
-			break;
-		case 'a':
-			wdata->creation_disposition = OPEN_ALWAYS;
-			break;
-		case 'x':
-			wdata->creation_disposition = CREATE_NEW;
-			break;
-	}
-	mode ++;
-
-	if (*mode && (*mode != '+')) {
-		mode++;
-	}
-
-	if (*mode && (*mode == '+')) {
-		wdata->desired_access = GENERIC_READ | GENERIC_WRITE;
-	} else if (OPEN_EXISTING == wdata->creation_disposition) {
-		wdata->desired_access = GENERIC_READ;
-	} else {
-		wdata->desired_access = GENERIC_WRITE;
-	}
-
-	wdata->handle = CreateFile(filename, wdata->desired_access, 0,
-			NULL, wdata->creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL);
-	if (INVALID_HANDLE_VALUE == wdata->handle) {
-		err = GetLastError();
-		switch (err) {
-			case ERROR_FILE_EXISTS:
-				php_error_docref(NULL TSRMLS_CC, E_WARNING, "File exists!");
-				return 0;
-
-			case ERROR_FILE_NOT_FOUND:
-				/* ERROR_FILE_NOT_FOUND with TRUNCATE_EXISTING means that
-				 * the file doesn't exist so now try to create it. */
-				if (TRUNCATE_EXISTING == wdata->creation_disposition) {
-					php_error_docref(NULL TSRMLS_CC, E_NOTICE, "File does not exist, creating new file!");
-
-					wdata->handle = CreateFile(filename, wdata->desired_access, 0,
-								NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
-					if (INVALID_HANDLE_VALUE == wdata->handle) {
-						dio_last_error_php_error(E_WARNING, "CreateFile() failed:" TSRMLS_CC);
-						return 0;
-					}
-				} else {
-					php_error_docref(NULL TSRMLS_CC, E_WARNING, "File not found!");
-					return 0;
-				}
-				break;
-
-			default:
-				dio_last_error_php_error(E_WARNING, "CreateFile() failed:" TSRMLS_CC);
-				return 0;
-		}
-	}
-
-	/* If canonical allocate the canonical buffer. */
-	if (data->canonical) {
-		wdata->canon_data = emalloc(sizeof(php_dio_win32_canon_data));
-		memset(wdata->canon_data, 0, sizeof(php_dio_win32_canon_data));
-		wdata->canon_data->size = DIO_WIN32_CANON_BUF_SIZE;
-	}
-
-	return 1;
-}
-/* }}} */
-
-/* {{{ dio_serial_init
- * Initialises the serial port
- */
-static int dio_serial_init(php_dio_stream_data *data TSRMLS_DC) {
-	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
-	DWORD rate_def, data_bits_def, stop_bits_def, parity_def;
-	DCB dcb;
-
-	if (!dio_data_rate_to_define(data->data_rate, &rate_def)) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid data_rate value (%d)", data->data_rate);
-		return 0;
-	}
-
-	if (!dio_data_bits_to_define(data->data_bits, &data_bits_def)) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid data_bits value (%d)", data->data_bits);
-		return 0;
-	}
-
-	if (!dio_stop_bits_to_define(data->stop_bits, &stop_bits_def)) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid stop_bits value (%d)", data->stop_bits);
-		return 0;
-	}
-
-	if (!dio_parity_to_define(data->parity, &parity_def)) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid parity value (%d)", data->parity);
-		return 0;
-	}
-
-	if (!GetCommState(wdata->handle, &(wdata->olddcb))) {
-		dio_last_error_php_error(E_WARNING, "GetCommState() failed:" TSRMLS_CC);
-		return 0;
-	}
-
-	/* Init the DCB structure */
-	memset(&dcb, 0, sizeof(DCB));
-	dcb.DCBlength = sizeof(DCB);
-
-	/* Set the communication parameters */
-	dcb.fBinary  = 1;
-	dcb.BaudRate = rate_def;
-	dcb.ByteSize = (BYTE)data_bits_def;
-	dcb.StopBits = (BYTE)stop_bits_def;
-	dcb.Parity   = (BYTE)parity_def;
-
-	/* Set the control line parameters */
-	dcb.fDtrControl       = DTR_CONTROL_DISABLE;
-	dcb.fDsrSensitivity   = FALSE;
-	dcb.fOutxDsrFlow      = FALSE;
-	dcb.fTXContinueOnXoff = FALSE;
-	dcb.fOutX             = FALSE;
-	dcb.fInX              = FALSE;
-	dcb.fErrorChar        = FALSE;
-	dcb.fNull             = FALSE;
-	dcb.fAbortOnError     = FALSE;
-
-	/* Hardware flow control */
-	if (data->flow_control) {
-		dcb.fOutxCtsFlow = TRUE;
-		dcb.fRtsControl  = RTS_CONTROL_HANDSHAKE;
-	} else {
-		dcb.fOutxCtsFlow = FALSE;
-		dcb.fRtsControl  = RTS_CONTROL_DISABLE;
-	}
-
-	if (!SetCommState(wdata->handle, &dcb)) {
-		dio_last_error_php_error(E_WARNING, "SetCommState() failed:" TSRMLS_CC);
-		return 0;
-	}
-
-	return 1;
-}
-/* }}} */
-
-
-/* {{{ dio_serial_uninit
- * Restores the serial settings back to their original state.
- */
-int dio_serial_uninit(php_dio_stream_data *data) {
-	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
-
-	if (!SetCommState(wdata->handle, &(wdata->olddcb))) {
-		return 0;
-	}
-
-	return 1;
-}
-/* }}} */
-
-/* {{{ dio_serial_flush
- * Purges the serial buffers of data.
- */
-int dio_serial_purge(php_dio_stream_data *data) {
-	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
-	BOOL ret;
-
-	/* Purge the canonical buffer if required */
-	if (data->canonical && ((wdata->desired_access & GENERIC_READ) == GENERIC_READ)) {
-		wdata->canon_data->read_pos  = 0;
-		wdata->canon_data->write_pos = 0;
-	}
-
-	/* Purge the com port */
-	if ((wdata->desired_access & (GENERIC_READ|GENERIC_WRITE)) == (GENERIC_READ|GENERIC_WRITE)) {
-		ret = PurgeComm(wdata->handle, PURGE_RXCLEAR|PURGE_TXCLEAR);
-	} else if ((wdata->desired_access & GENERIC_WRITE) == GENERIC_WRITE) {
-		ret = PurgeComm(wdata->handle, PURGE_TXCLEAR);
-	} else if ((wdata->desired_access & GENERIC_READ) == GENERIC_READ) {
-		ret = PurgeComm(wdata->handle, PURGE_RXCLEAR);
-	}
-
-	return ret;
-}
-/* }}} */
-
-/* {{{ dio_serial_open_stream
- * Opens the underlying stream.
- */
-int dio_serial_open_stream(char *filename, char *mode, php_dio_stream_data *data TSRMLS_DC) {
-	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
-	COMMTIMEOUTS cto = { 0, 0, 0, 0, 0 };
-
-	php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Opening \"%s\" as a serial port (mode=\"%s\").", filename, mode);
-
-	if (*mode != 'r') {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING, "You must open serial ports in read or read/write mode!");
-		return 0;
-	}
-
-	if (!dio_raw_open_stream(filename, mode, data TSRMLS_CC)) {
-		return 0;
-	}
-
-	if (!GetCommTimeouts(wdata->handle, &(wdata->oldcto))) {
-		dio_last_error_php_error(E_WARNING, "GetCommTimeouts() failed (Not a comm port?):" TSRMLS_CC);
-		CloseHandle(wdata->handle);
-		return 0;
-	}
-
-	/* If we're not blocking but don't have a timeout
-	   set to return immediately */
-	if (!data->is_blocking && !data->has_timeout) {
-		cto.ReadIntervalTimeout = MAXDWORD;
-	}
-	
-	/* If we have a timeout ignore the blocking and set
-	   the total time in which to read the data */
-	if (data->has_timeout) {
-		cto.ReadIntervalTimeout = MAXDWORD;
-		cto.ReadTotalTimeoutMultiplier  = MAXDWORD;
-		cto.ReadTotalTimeoutConstant = (data->timeout_usec / 1000) + 
-			(data->timeout_sec * 1000);
-	}
-
-	if (!SetCommTimeouts(wdata->handle, &cto)) {
-		dio_last_error_php_error(E_WARNING, "SetCommTimeouts() failed:" TSRMLS_CC);
-		CloseHandle(wdata->handle);
-		return 0;
-	}
-
-	if (!dio_serial_init(data TSRMLS_CC)) {
-		CloseHandle(wdata->handle);
-		return 0;
-	}
-
-	return 1;
-}
-/* }}} */
-
-/*
- * Local variables:
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim600: fdm=marker
- * vim: sw=4 ts=4 noet
- */
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 5                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 2009 Melanie Rhianna Lewis                             |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.0 of the PHP license,       |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_0.txt.                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Author: Melanie Rhianna Lewis <cyberspice@php.net>                   |
+   +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_dio_common.h"
+
+#ifndef ZEND_WIN32
+#error ZEND_WIN32 not defined!
+#endif
+
+/* {{{ dio_last_error_php_error
+ * Generates a PHP error message based upon the last Windows error.
+ */
+static void dio_last_error_php_error(int level, char * message TSRMLS_DC) {
+	LPVOID msgbuf;
+	DWORD  msgbuflen;
+	char * errmsg;
+	DWORD  err;
+
+#ifdef UNICODE
+	DWORD  errmsglen;
+#endif
+
+	err = GetLastError();
+	msgbuflen = FormatMessage(
+		FORMAT_MESSAGE_ALLOCATE_BUFFER|
+		FORMAT_MESSAGE_FROM_SYSTEM|
+		FORMAT_MESSAGE_IGNORE_INSERTS,
+		NULL,
+		err,
+		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+		(LPTSTR)&msgbuf,
+		0,
+		NULL);
+
+#ifdef UNICODE
+
+	/* Get the length of the converted message */
+	errmsglen = WideCharToMultibyte(
+		CP_ACP,
+		0
+		(LPCWSTR)msgbuf,
+		-1,
+		(LPSTR)errmsg,
+		0,
+		NULL,
+		NULL);
+
+	/* Allocate a buffer */
+	errmsg = emalloc(errmsglen);
+	if (!errmsg) {
+		php_error_docref(NULL TSRMLS_CC, E_ERROR, "Out of memory in dio_last_error_php_error()!");
+		LocalFree(msgbuf);
+		return;
+	}
+
+	/* Convert the message */
+	errmsglen = WideCharToMultibyte(
+		CP_ACP,
+		0
+		(LPCWSTR)msgbuf,
+		-1,
+		(LPSTR)errmsg,
+		errmsglen,
+		NULL,
+		NULL);
+
+#else
+	errmsg = (char *)msgbuf;
+#endif
+
+	php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s[ERROR %d] %s", message, err, errmsg);
+
+	LocalFree(msgbuf);
+#ifdef UNICODE
+	efree(errmsg);
+#endif
+}
+
+/* {{{ dio_data_rate_to_define
+ * Converts a numeric data rate to a termios define
+ */
+static int dio_data_rate_to_define(long rate, DWORD *def) {
+	switch (rate) {
+		case 75:
+		case 110:
+		case 134:
+		case 150:
+		case 300:
+		case 600:
+		case 1200:
+		case 1800:
+		case 2400:
+		case 4800:
+		case 7200:
+		case 9600:
+		case 14400:
+		case 19200:
+		case 38400:
+		case 57600:
+		case 115200:
+		case 56000:
+		case 128000:
+		case 256000:
+			break;
+		default:
+			return 0;
+	}
+
+	*def = (DWORD)rate;
+	return 1;
+}
+/* }}} */
+
+
+/* {{{ dio_data_bits_to_define
+ * Converts a number of data bits to a termios define
+ */
+static int dio_data_bits_to_define(int data_bits, DWORD *def) {
+	switch (data_bits) {
+		case 8:
+		case 7:
+		case 6:
+		case 5:
+		case 4:
+			break;
+		default:
+			return 0;
+	}
+
+	*def = (DWORD)data_bits;
+	return 1;
+}
+/* }}} */
+
+/* {{{ dio_stop_bits_to_define
+ * Converts a number of stop bits to a termios define
+ */
+static int dio_stop_bits_to_define(int stop_bits, DWORD *def) {
+	DWORD val;
+
+	switch (stop_bits) {
+		case 1:
+			val = ONESTOPBIT;
+			break;
+		case 2:
+			val = TWOSTOPBITS;
+			break;
+		case 3:
+			val = ONE5STOPBITS;
+			break;
+		default:
+			return 0;
+	}
+
+	*def = val;
+	return 1;
+}
+/* }}} */
+
+/* {{{ dio_parity_to_define
+ * Converts a parity type to a termios define
+ */
+static int dio_parity_to_define(int parity, DWORD *def) {
+	DWORD val;
+
+	switch (parity) {
+		case 0:
+			val = NOPARITY;
+			break;
+		case 1:
+			val = ODDPARITY;
+			break;
+		case 2:
+			val = EVENPARITY;
+			break;
+		default:
+			return 0;
+	}
+
+	*def = val;
+	return 1;
+}
+/* }}} */
+
+/* {{{ dio_create_stream_data
+ * Creates an initialised stream data structure.  Free with efree().
+ */
+php_dio_stream_data * dio_create_stream_data(void) {
+	php_dio_win32_stream_data * data = emalloc(sizeof(php_dio_win32_stream_data));
+	memset(data, 0, sizeof(php_dio_win32_stream_data));
+	dio_init_stream_data(&(data->common));
+	data->handle = INVALID_HANDLE_VALUE;
+	data->desired_access = 0;
+	data->creation_disposition = 0;
+	data->olddcb.DCBlength = sizeof(DCB);
+
+	return (php_dio_stream_data *)data;
+}
+/* }}} */
+
+/* {{{ dio_common_write
+ * Writes count chars from the buffer to the stream described by the stream data.
+ */
+size_t dio_common_write(php_dio_stream_data *data, const char *buf, size_t count) {
+	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
+	DWORD total = 0;
+
+	if (WriteFile(wdata->handle, buf, (DWORD)count, &total, NULL)) {
+		return (size_t)total;
+	}
+
+	return 0;
+}
+/* }}} */
+
+/* {{{ dio_buffer_read
+ * Reads any available chars from the canonical buffer.
+ */
+static size_t dio_buffer_read(php_dio_win32_stream_data *wdata, const char *buf, size_t count) {
+	php_dio_win32_canon_data *canon_data = wdata->canon_data;
+	size_t total = 0;
+
+	/* Read always follows write.  I.e. if read ptr > write ptr buffer has
+	   wrapped and so we need to copy two blocks of data. */
+	if (canon_data->read_pos > canon_data->write_pos) {
+
+		/* Check we actually need to copy both blocks */
+		if ((canon_data->size - canon_data->read_pos) > count) {
+
+			/* No we don't.  Just copy as much as we were asked for. */
+			memcpy((char*)buf, 
+				   &(canon_data->buf[canon_data->read_pos]), 
+				   count);
+			/* Update the read pointer. */
+			canon_data->read_pos += count;
+
+			/* Return the amount read. */
+			return count;
+		} else {
+
+			/* We need to copy both blocks so copy data up to the end of 
+			   the buffer. */
+			total = canon_data->size - canon_data->read_pos;
+			memcpy((char*)buf, 
+				   &(canon_data->buf[canon_data->read_pos]), 
+				   total);
+			canon_data->read_pos = 0;
+			count -= total;
+
+			/* Now copy the data from the start of the buffer either up
+			   count or the number of bytes in the buffer. */
+
+			if (canon_data->write_pos > count) {
+				memcpy((char*)buf, canon_data->buf, count);
+				canon_data->read_pos = count;
+				total += count;
+
+				return total;
+			} else {
+				memcpy((char*)buf, canon_data->buf, canon_data->write_pos);
+				canon_data->read_pos = canon_data->write_pos;
+				total += canon_data->write_pos;
+
+				return total;
+			}
+		}
+
+	/* Else if write follows read.  This is a simpler case.  We just copy 
+	   either all the data buffered or count, which ever is smaller. */
+	} else if (canon_data->write_pos > canon_data->read_pos) {
+		if ((canon_data->write_pos - canon_data->read_pos) > count) {
+			memcpy((char*)buf, 
+				   &(canon_data->buf[canon_data->read_pos]), 
+				   count);
+			canon_data->read_pos += count;
+
+			return count;
+		} else {
+			total = canon_data->write_pos - canon_data->read_pos;
+			memcpy((char*)buf, 
+				   &(canon_data->buf[canon_data->read_pos]), 
+				   total);
+			canon_data->read_pos += total;
+
+			return total;
+		}
+	}
+
+	/* Else we need to read more data from the data port. */
+	return 0;
+}
+
+/* {{{ dio_com_read
+ * Read chars from the data port.
+ */
+static size_t dio_com_read(php_dio_stream_data *data, const char *buf, size_t count) {
+	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
+	DWORD err, total = 0;
+
+	if (ReadFile(wdata->handle, (void*)buf, (DWORD)count, &total, NULL)) {
+
+		if (total) {
+			return (size_t)total;
+		}
+
+		data->end_of_file = 1;
+	}
+
+	if (!data->end_of_file) {
+		err = GetLastError();
+
+		if (ERROR_HANDLE_EOF == err) {
+			data->end_of_file = 1;
+		}
+	}
+
+	return 0;
+}
+
+/* {{{ dio_canonical_read
+ * Reads chars from the input stream until the internal buffer is full or a new
+ * line is reached.
+ */
+static size_t dio_canonical_read(php_dio_win32_stream_data *wdata, const char *buf, size_t count) {
+	php_dio_win32_canon_data *canon_data = wdata->canon_data;
+	size_t total = 0;
+	char ch;
+
+	/* See if there's any buffered data and copy it. */
+	total = dio_buffer_read(wdata, buf, count);
+	if (total) {
+		return total;
+	}
+
+	/* Need to read more data from the data port.  Buffer should be empty(er)
+	   by now. */
+	do {
+		/* Is the buffer full? */
+		if (((canon_data->write_pos + 1) % canon_data->size) == 
+			canon_data->read_pos) {
+			break;
+		}
+
+		/* Read a byte from the input checking for EOF. */
+		if (!dio_com_read((php_dio_stream_data*)wdata, &ch, 1)) {
+			break;
+		}
+
+		/* Translate CR to newlines (same as ICRNL in POSIX) */
+		ch = (ch != '\r') ? ch : '\n';
+
+		/* We read a character!  So buffer it. */
+		canon_data->buf[canon_data->write_pos++] = ch;
+		if (canon_data->write_pos >= canon_data->size) {
+			canon_data->write_pos = 0;
+		}
+
+		/* End of line/input (^D)? */
+	} while ((ch != '\n') && (ch != 0x04));
+
+	return dio_buffer_read(wdata, buf, count);
+}
+/* }}} */
+
+/* {{{ dio_common_read
+ * Reads count chars to the buffer to the stream described by the stream data.
+ */
+size_t dio_common_read(php_dio_stream_data *data, const char *buf, size_t count) {
+
+	/* You ask for no bytes you'll get none :-) */
+	if (!count) {
+		return 0;
+	}
+
+	if (data->canonical) {
+		return dio_canonical_read((php_dio_win32_stream_data*)data, buf, count);
+	} else {
+		return dio_com_read(data, buf, count);
+	}
+}
+/* }}} */
+
+/* {{{ php_dio_stream_data
+ * Closes the php_stream.
+ */
+int dio_common_close(php_dio_stream_data *data) {
+	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
+
+	if (data->canonical) {
+		efree(wdata->canon_data);
+	}
+
+	if (!CloseHandle(wdata->handle)) {
+		return 0;
+	}
+
+	return 1;
+}
+/* }}} */
+
+/* {{{ dio_common_set_option
+ * Sets/gets stream options
+ */
+int dio_common_set_option(php_dio_stream_data *data, int option, int value, void *ptrparam) {
+	COMMTIMEOUTS cto = { 0, 0, 0, 0, 0 };
+	int old_is_blocking = 0;
+
+	/* Can't do timeouts or non blocking with raw windows streams :-( */
+	if (DIO_STREAM_TYPE_SERIAL == data->stream_type) {
+		switch (option) {
+			case PHP_STREAM_OPTION_BLOCKING:
+				old_is_blocking   = data->is_blocking;
+				data->is_blocking = value ? 1 : 0;
+
+				/* Only change values if we need to change them. */
+				if (data->is_blocking != old_is_blocking) {
+					/* If we're not blocking but don't have a timeout
+					   set to return immediately */
+					if (!data->is_blocking && !data->has_timeout) {
+						cto.ReadIntervalTimeout = MAXDWORD;
+					}
+
+					/* If we have a timeout ignore the blocking and set
+					   the total time in which to read the data */
+					if (data->has_timeout) {
+						cto.ReadIntervalTimeout = MAXDWORD;
+						cto.ReadTotalTimeoutMultiplier  = MAXDWORD;
+						cto.ReadTotalTimeoutConstant = (data->timeout_usec / 1000) +
+							(data->timeout_sec * 1000);
+					}
+
+					if (!SetCommTimeouts(((php_dio_win32_stream_data*)data)->handle, &cto)) {
+						return PHP_STREAM_OPTION_RETURN_ERR;
+					}
+				}
+				return old_is_blocking ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
+
+			case PHP_STREAM_OPTION_READ_TIMEOUT:
+				if (ptrparam) {
+					/* struct timeval is supported with PHP_WIN32 defined. */
+					struct timeval *tv = (struct timeval*)ptrparam;
+
+					/* A timeout of zero seconds and zero microseconds disables
+					   any existing timeout. */
+					if (tv->tv_sec || tv->tv_usec) {
+						data->timeout_sec = tv->tv_sec;
+						data->timeout_usec = tv->tv_usec;
+						data->has_timeout = -1;
+
+						cto.ReadIntervalTimeout = MAXDWORD;
+						cto.ReadTotalTimeoutMultiplier  = MAXDWORD;
+						cto.ReadTotalTimeoutConstant = (data->timeout_usec / 1000) +
+							(data->timeout_sec * 1000);
+					} else {
+						data->timeout_sec = 0;
+						data->timeout_usec = 0;
+						data->has_timeout = 0;
+						data->timed_out = 0;
+
+						/* If we're not blocking but don't have a timeout
+						   set to return immediately */
+						if (!data->is_blocking) {
+							cto.ReadIntervalTimeout = MAXDWORD;
+						}
+					}
+
+					if (!SetCommTimeouts(((php_dio_win32_stream_data*)data)->handle, &cto)) {
+						return PHP_STREAM_OPTION_RETURN_ERR;
+					} else {
+						return PHP_STREAM_OPTION_RETURN_OK;
+					}
+				} else {
+					return PHP_STREAM_OPTION_RETURN_ERR;
+				}
+
+			default:
+				break;
+		}
+	}
+
+	return 1;
+}
+/* }}} */
+
+/* {{{ dio_raw_open_stream
+ * Opens the underlying stream.
+ */
+int dio_raw_open_stream(char *filename, char *mode, php_dio_stream_data *data TSRMLS_DC) {
+	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
+	DWORD err;
+
+	switch(*mode) {
+		case 'r':
+			wdata->creation_disposition = OPEN_EXISTING;
+			break;
+		case 'w':
+			wdata->creation_disposition = TRUNCATE_EXISTING;
+			break;
+		case 'a':
+			wdata->creation_disposition = OPEN_ALWAYS;
+			break;
+		case 'x':
+			wdata->creation_disposition = CREATE_NEW;
+			break;
+	}
+	mode ++;
+
+	if (*mode && (*mode != '+')) {
+		mode++;
+	}
+
+	if (*mode && (*mode == '+')) {
+		wdata->desired_access = GENERIC_READ | GENERIC_WRITE;
+	} else if (OPEN_EXISTING == wdata->creation_disposition) {
+		wdata->desired_access = GENERIC_READ;
+	} else {
+		wdata->desired_access = GENERIC_WRITE;
+	}
+
+	wdata->handle = CreateFile(filename, wdata->desired_access, 0,
+			NULL, wdata->creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL);
+	if (INVALID_HANDLE_VALUE == wdata->handle) {
+		err = GetLastError();
+		switch (err) {
+			case ERROR_FILE_EXISTS:
+				php_error_docref(NULL TSRMLS_CC, E_WARNING, "File exists!");
+				return 0;
+
+			case ERROR_FILE_NOT_FOUND:
+				/* ERROR_FILE_NOT_FOUND with TRUNCATE_EXISTING means that
+				 * the file doesn't exist so now try to create it. */
+				if (TRUNCATE_EXISTING == wdata->creation_disposition) {
+					php_error_docref(NULL TSRMLS_CC, E_NOTICE, "File does not exist, creating new file!");
+
+					wdata->handle = CreateFile(filename, wdata->desired_access, 0,
+								NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+					if (INVALID_HANDLE_VALUE == wdata->handle) {
+						dio_last_error_php_error(E_WARNING, "CreateFile() failed:" TSRMLS_CC);
+						return 0;
+					}
+				} else {
+					php_error_docref(NULL TSRMLS_CC, E_WARNING, "File not found!");
+					return 0;
+				}
+				break;
+
+			default:
+				dio_last_error_php_error(E_WARNING, "CreateFile() failed:" TSRMLS_CC);
+				return 0;
+		}
+	}
+
+	/* If canonical allocate the canonical buffer. */
+	if (data->canonical) {
+		wdata->canon_data = emalloc(sizeof(php_dio_win32_canon_data));
+		memset(wdata->canon_data, 0, sizeof(php_dio_win32_canon_data));
+		wdata->canon_data->size = DIO_WIN32_CANON_BUF_SIZE;
+	}
+
+	return 1;
+}
+/* }}} */
+
+/* {{{ dio_serial_init
+ * Initialises the serial port
+ */
+static int dio_serial_init(php_dio_stream_data *data TSRMLS_DC) {
+	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
+	DWORD rate_def, data_bits_def, stop_bits_def, parity_def;
+	DCB dcb;
+
+	if (!dio_data_rate_to_define(data->data_rate, &rate_def)) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid data_rate value (%d)", data->data_rate);
+		return 0;
+	}
+
+	if (!dio_data_bits_to_define(data->data_bits, &data_bits_def)) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid data_bits value (%d)", data->data_bits);
+		return 0;
+	}
+
+	if (!dio_stop_bits_to_define(data->stop_bits, &stop_bits_def)) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid stop_bits value (%d)", data->stop_bits);
+		return 0;
+	}
+
+	if (!dio_parity_to_define(data->parity, &parity_def)) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid parity value (%d)", data->parity);
+		return 0;
+	}
+
+	if (!GetCommState(wdata->handle, &(wdata->olddcb))) {
+		dio_last_error_php_error(E_WARNING, "GetCommState() failed:" TSRMLS_CC);
+		return 0;
+	}
+
+	/* Init the DCB structure */
+	memset(&dcb, 0, sizeof(DCB));
+	dcb.DCBlength = sizeof(DCB);
+
+	/* Set the communication parameters */
+	dcb.fBinary  = 1;
+	dcb.BaudRate = rate_def;
+	dcb.ByteSize = (BYTE)data_bits_def;
+	dcb.StopBits = (BYTE)stop_bits_def;
+	dcb.Parity   = (BYTE)parity_def;
+
+	/* Set the control line parameters */
+	dcb.fDtrControl       = DTR_CONTROL_DISABLE;
+	dcb.fDsrSensitivity   = FALSE;
+	dcb.fOutxDsrFlow      = FALSE;
+	dcb.fTXContinueOnXoff = FALSE;
+	dcb.fOutX             = FALSE;
+	dcb.fInX              = FALSE;
+	dcb.fErrorChar        = FALSE;
+	dcb.fNull             = FALSE;
+	dcb.fAbortOnError     = FALSE;
+
+	/* Hardware flow control */
+	if (data->flow_control) {
+		dcb.fOutxCtsFlow = TRUE;
+		dcb.fRtsControl  = RTS_CONTROL_HANDSHAKE;
+	} else {
+		dcb.fOutxCtsFlow = FALSE;
+		dcb.fRtsControl  = RTS_CONTROL_DISABLE;
+	}
+
+	if (!SetCommState(wdata->handle, &dcb)) {
+		dio_last_error_php_error(E_WARNING, "SetCommState() failed:" TSRMLS_CC);
+		return 0;
+	}
+
+	return 1;
+}
+/* }}} */
+
+
+/* {{{ dio_serial_uninit
+ * Restores the serial settings back to their original state.
+ */
+int dio_serial_uninit(php_dio_stream_data *data) {
+	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
+
+	if (!SetCommState(wdata->handle, &(wdata->olddcb))) {
+		return 0;
+	}
+
+	return 1;
+}
+/* }}} */
+
+/* {{{ dio_serial_flush
+ * Purges the serial buffers of data.
+ */
+int dio_serial_purge(php_dio_stream_data *data) {
+	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
+	BOOL ret;
+
+	/* Purge the canonical buffer if required */
+	if (data->canonical && ((wdata->desired_access & GENERIC_READ) == GENERIC_READ)) {
+		wdata->canon_data->read_pos  = 0;
+		wdata->canon_data->write_pos = 0;
+	}
+
+	/* Purge the com port */
+	if ((wdata->desired_access & (GENERIC_READ|GENERIC_WRITE)) == (GENERIC_READ|GENERIC_WRITE)) {
+		ret = PurgeComm(wdata->handle, PURGE_RXCLEAR|PURGE_TXCLEAR);
+	} else if ((wdata->desired_access & GENERIC_WRITE) == GENERIC_WRITE) {
+		ret = PurgeComm(wdata->handle, PURGE_TXCLEAR);
+	} else if ((wdata->desired_access & GENERIC_READ) == GENERIC_READ) {
+		ret = PurgeComm(wdata->handle, PURGE_RXCLEAR);
+	}
+
+	return ret;
+}
+/* }}} */
+
+/* {{{ dio_serial_open_stream
+ * Opens the underlying stream.
+ */
+int dio_serial_open_stream(char *filename, char *mode, php_dio_stream_data *data TSRMLS_DC) {
+	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
+	COMMTIMEOUTS cto = { 0, 0, 0, 0, 0 };
+
+	php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Opening \"%s\" as a serial port (mode=\"%s\").", filename, mode);
+
+	if (*mode != 'r') {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "You must open serial ports in read or read/write mode!");
+		return 0;
+	}
+
+	if (!dio_raw_open_stream(filename, mode, data TSRMLS_CC)) {
+		return 0;
+	}
+
+	if (!GetCommTimeouts(wdata->handle, &(wdata->oldcto))) {
+		dio_last_error_php_error(E_WARNING, "GetCommTimeouts() failed (Not a comm port?):" TSRMLS_CC);
+		CloseHandle(wdata->handle);
+		return 0;
+	}
+
+	/* If we're not blocking but don't have a timeout
+	   set to return immediately */
+	if (!data->is_blocking && !data->has_timeout) {
+		cto.ReadIntervalTimeout = MAXDWORD;
+	}
+	
+	/* If we have a timeout ignore the blocking and set
+	   the total time in which to read the data */
+	if (data->has_timeout) {
+		cto.ReadIntervalTimeout = MAXDWORD;
+		cto.ReadTotalTimeoutMultiplier  = MAXDWORD;
+		cto.ReadTotalTimeoutConstant = (data->timeout_usec / 1000) + 
+			(data->timeout_sec * 1000);
+	}
+
+	if (!SetCommTimeouts(wdata->handle, &cto)) {
+		dio_last_error_php_error(E_WARNING, "SetCommTimeouts() failed:" TSRMLS_CC);
+		CloseHandle(wdata->handle);
+		return 0;
+	}
+
+	if (!dio_serial_init(data TSRMLS_CC)) {
+		CloseHandle(wdata->handle);
+		return 0;
+	}
+
+	return 1;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim600: fdm=marker
+ * vim: sw=4 ts=4 noet
+ */
--- a/php_dio_posix.h
+++ b/php_dio_posix.h
@@ -1,70 +1,70 @@
-/*
-   +----------------------------------------------------------------------+
-   | PHP Version 5                                                        |
-   +----------------------------------------------------------------------+
-   | Copyright (c) 2009 Melanie Rhianna Lewis                             |
-   +----------------------------------------------------------------------+
-   | This source file is subject to version 3.0 of the PHP license,       |
-   | that is bundled with this package in the file LICENSE, and is        |
-   | available through the world-wide-web at the following url:           |
-   | http://www.php.net/license/3_0.txt.                                  |
-   | If you did not receive a copy of the PHP license and are unable to   |
-   | obtain it through the world-wide-web, please send a note to          |
-   | license@php.net so we can mail you a copy immediately.               |
-   +----------------------------------------------------------------------+
-   | Author: Melanie Rhianna Lewis <cyberspice@php.net>                   |
-   +----------------------------------------------------------------------+
- */
-
-#ifndef PHP_DIO_POSIX_H_
-#define PHP_DIO_POSIX_H_
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/select.h>
-#include <sys/time.h>
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#include <fcntl.h>
-#include <termios.h>
-
-
-/**
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 5                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 2009 Melanie Rhianna Lewis                             |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.0 of the PHP license,       |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_0.txt.                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Author: Melanie Rhianna Lewis <cyberspice@php.net>                   |
+   +----------------------------------------------------------------------+
+ */
+
+#ifndef PHP_DIO_POSIX_H_
+#define PHP_DIO_POSIX_H_
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/time.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <fcntl.h>
+#include <termios.h>
+
+
+/**
  * Detect if we can support non blocking IO.
- */
-#ifdef O_NONBLOCK
-#define DIO_NONBLOCK O_NONBLOCK
-#else
-#ifdef O_NDELAY
-#define DIO_NONBLOCK O_NDELAY
-#endif
-#endif
-
-/**
+ */
+#ifdef O_NONBLOCK
+#define DIO_NONBLOCK O_NONBLOCK
+#else
+#ifdef O_NDELAY
+#define DIO_NONBLOCK O_NDELAY
+#endif
+#endif
+
+/**
  * POSIXy platforms have file permissions
- */
-#define DIO_HAS_FILEPERMS
-
-#include "php_dio_common_data.h"
-
-typedef struct _php_dio_posix_stream_data {
-	php_dio_stream_data common;
-	int fd;
-	int flags;
-	/* Serial options */
-	struct termios oldtio;
-} php_dio_posix_stream_data ;
-
-#endif /* PHP_DIO_POSIX_H_ */
-
-/*
- * Local variables:
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim600: fdm=marker
- * vim: sw=4 ts=4 noet
- */
+ */
+#define DIO_HAS_FILEPERMS
+
+#include "php_dio_common_data.h"
+
+typedef struct _php_dio_posix_stream_data {
+	php_dio_stream_data common;
+	int fd;
+	int flags;
+	/* Serial options */
+	struct termios oldtio;
+} php_dio_posix_stream_data ;
+
+#endif /* PHP_DIO_POSIX_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim600: fdm=marker
+ * vim: sw=4 ts=4 noet
+ */
--- a/php_dio_win32.h
+++ b/php_dio_win32.h
@@ -1,62 +1,62 @@
-/*
-   +----------------------------------------------------------------------+
-   | PHP Version 5                                                        |
-   +----------------------------------------------------------------------+
-   | Copyright (c) 2009 Melanie Rhianna Lewis                             |
-   +----------------------------------------------------------------------+
-   | This source file is subject to version 3.0 of the PHP license,       |
-   | that is bundled with this package in the file LICENSE, and is        |
-   | available through the world-wide-web at the following url:           |
-   | http://www.php.net/license/3_0.txt.                                  |
-   | If you did not receive a copy of the PHP license and are unable to   |
-   | obtain it through the world-wide-web, please send a note to          |
-   | license@php.net so we can mail you a copy immediately.               |
-   +----------------------------------------------------------------------+
-   | Author: Melanie Rhianna Lewis <cyberspice@php.net>                   |
-   +----------------------------------------------------------------------+
- */
-
-#ifndef PHP_DIO_WIN32_H_
-#define PHP_DIO_WIN32_H_
-
-#include <windows.h>
-
-/* Windows platform can do non blocking. */
-#define DIO_NONBLOCK
-
-#include "php_dio_common_data.h"
-
-#define DIO_WIN32_CANON_BUF_SIZE 8192
-
-/* This is the buffer information when reading in canonical mode.  Data is 
-   read right up to either buffer being full or a newline being read.  Excess
-   data will be retained in the buffer until the next read. */
-typedef struct _php_dio_win32_canon_data {
-	size_t size;
-	size_t read_pos;
-	size_t write_pos;
-	char buf[DIO_WIN32_CANON_BUF_SIZE];
-
-} php_dio_win32_canon_data;
-
-typedef struct _php_dio_win32_stream_data {
-	php_dio_stream_data common;
-	HANDLE handle;
-	DWORD desired_access;
-	DWORD creation_disposition;
-	DCB olddcb;
-	COMMTIMEOUTS oldcto;
-	php_dio_win32_canon_data *canon_data;
-
-} php_dio_win32_stream_data ;
-
-#endif /* PHP_DIO_WIN32_H_ */
-
-/*
- * Local variables:
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim600: fdm=marker
- * vim: sw=4 ts=4 noet
- */
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 5                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 2009 Melanie Rhianna Lewis                             |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.0 of the PHP license,       |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_0.txt.                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Author: Melanie Rhianna Lewis <cyberspice@php.net>                   |
+   +----------------------------------------------------------------------+
+ */
+
+#ifndef PHP_DIO_WIN32_H_
+#define PHP_DIO_WIN32_H_
+
+#include <windows.h>
+
+/* Windows platform can do non blocking. */
+#define DIO_NONBLOCK
+
+#include "php_dio_common_data.h"
+
+#define DIO_WIN32_CANON_BUF_SIZE 8192
+
+/* This is the buffer information when reading in canonical mode.  Data is 
+   read right up to either buffer being full or a newline being read.  Excess
+   data will be retained in the buffer until the next read. */
+typedef struct _php_dio_win32_canon_data {
+	size_t size;
+	size_t read_pos;
+	size_t write_pos;
+	char buf[DIO_WIN32_CANON_BUF_SIZE];
+
+} php_dio_win32_canon_data;
+
+typedef struct _php_dio_win32_stream_data {
+	php_dio_stream_data common;
+	HANDLE handle;
+	DWORD desired_access;
+	DWORD creation_disposition;
+	DCB olddcb;
+	COMMTIMEOUTS oldcto;
+	php_dio_win32_canon_data *canon_data;
+
+} php_dio_win32_stream_data ;
+
+#endif /* PHP_DIO_WIN32_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim600: fdm=marker
+ * vim: sw=4 ts=4 noet
+ */
