From: Michael Heimpold Date: Wed, 25 Jul 2012 20:41:58 +0000 (+0000) Subject: [packages] php5: update pecl module dio to 0.0.6 X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fsvn-archive%2Farchive.git;a=commitdiff_plain;hb=d6a68c38bc3c6d0e157853e9ebdec20aee510679 [packages] php5: update pecl module dio to 0.0.6 Signed-off-by: Michael Heimpold SVN-Revision: 32875 --- diff --git a/lang/php5/patches/010-PECL-dio.patch b/lang/php5/patches/010-PECL-dio.patch index 40867ed848..e3b22c4da9 100644 --- a/lang/php5/patches/010-PECL-dio.patch +++ b/lang/php5/patches/010-PECL-dio.patch @@ -13,7 +13,7 @@ +fi --- /dev/null +++ b/ext/dio/dio.c -@@ -0,0 +1,801 @@ +@@ -0,0 +1,871 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | @@ -112,7 +112,7 @@ + return; + } + -+ if (php_check_open_basedir(file_name TSRMLS_CC) || (PG(safe_mode) && !php_checkuid(file_name, "wb+", CHECKUID_CHECK_MODE_PARAM))) { ++ if (php_check_open_basedir(file_name TSRMLS_CC) || DIO_SAFE_MODE_CHECK(file_name, "wb+")) { + RETURN_FALSE; + } + @@ -127,14 +127,73 @@ + RETURN_FALSE; + } + ++ if (!new_php_fd(&f, fd)) { ++ RETURN_FALSE; ++ } ++ ++ ZEND_REGISTER_RESOURCE(return_value, f, le_fd); ++} ++/* }}} */ ++ ++#ifndef PHP_WIN32 ++ ++/* {{{ proto resource dio_fdopen(int fd) ++ Returns a resource for the specified file descriptor. */ ++PHP_FUNCTION(dio_fdopen) ++{ ++ php_fd_t *f; ++ long lfd; ++ int fd; ++ ++ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &lfd) == FAILURE) { ++ return; ++ } ++ ++ fd = (int)lfd; ++ ++ if ((fcntl(fd, F_GETFL, 0) == -1) && (errno == EBADF)) { ++ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Bad file descriptor %d", fd); ++ RETURN_FALSE; ++ } + + if (!new_php_fd(&f, fd)) { + RETURN_FALSE; + } ++ + ZEND_REGISTER_RESOURCE(return_value, f, le_fd); +} +/* }}} */ + ++ ++/* {{{ proto resource dio_dup(resource fd) ++ Opens a duplicate of the specified open resource. */ ++PHP_FUNCTION(dio_dup) ++{ ++ zval *r_fd; ++ php_fd_t *f, *df; ++ int dfd; ++ ++ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &r_fd) == FAILURE) { ++ return; ++ } ++ ++ ZEND_FETCH_RESOURCE(f, php_fd_t *, &r_fd, -1, le_fd_name, le_fd); ++ ++ dfd = dup(f->fd); ++ if (dfd == -1) { ++ php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot duplication file descriptor %d: %s", f->fd, strerror(errno)); ++ RETURN_FALSE; ++ } ++ ++ if (!new_php_fd(&df, dfd)) { ++ RETURN_FALSE; ++ } ++ ++ ZEND_REGISTER_RESOURCE(return_value, df, le_fd); ++} ++/* }}} */ ++#endif ++ +/* {{{ proto string dio_read(resource fd[, int n]) + Read n bytes from fd and return them, if n is not specified, read 1k */ +PHP_FUNCTION(dio_read) @@ -194,7 +253,7 @@ + + res = write(f->fd, data, trunc_len ? trunc_len : data_len); + if (res == -1) { -+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot write data to file descriptor %d, %s", f->fd, strerror(errno)); ++ php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot write data to file descriptor %d: %s", f->fd, strerror(errno)); + } + + RETURN_LONG(res); @@ -451,7 +510,7 @@ + } + + if (zend_hash_find(fh, "is_canonical", sizeof("is_canonical"), (void **) &element) == FAILURE) { -+ Is_Canonical = 1; ++ Is_Canonical = 0; + } else { + Is_Canonical = Z_LVAL_PP(element); + } @@ -664,6 +723,15 @@ + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + ++ZEND_BEGIN_ARG_INFO_EX(dio_fdopen_args, 0, 0, 1) ++ ZEND_ARG_INFO(0, fd) ++ZEND_END_ARG_INFO() ++ ++ZEND_BEGIN_ARG_INFO_EX(dio_dup_args, 0, 0, 1) ++ ZEND_ARG_INFO(0, fd) ++ZEND_END_ARG_INFO() ++ ++ +ZEND_BEGIN_ARG_INFO_EX(dio_read_args, 0, 0, 1) + ZEND_ARG_INFO(0, fd) + ZEND_ARG_INFO(0, n) @@ -725,12 +793,14 @@ + +static zend_object_handlers dio_raw_object_handlers; + -+static function_entry dio_functions[] = { ++static zend_function_entry dio_functions[] = { + /* Class functions. */ + + /* Legacy functions (Deprecated - See dio_legacy.c) */ + PHP_FE(dio_open, dio_open_args) +#ifndef PHP_WIN32 ++ PHP_FE(dio_fdopen, dio_fdopen_args) ++ PHP_FE(dio_dup, dio_dup_args) + PHP_FE(dio_truncate, dio_truncate_args) +#endif + PHP_FE(dio_stat, dio_stat_args) @@ -1051,6 +1121,668 @@ --- /dev/null +++ b/ext/dio/dio_posix.c @@ -0,0 +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 | ++ +----------------------------------------------------------------------+ ++ */ ++ ++#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 ++ */ +--- /dev/null ++++ b/ext/dio/dio_stream_wrappers.c +@@ -0,0 +1,406 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | @@ -1074,1001 +1806,175 @@ +#endif + +#include "php.h" ++#include "ext/standard/url.h" + ++#include "php_dio.h" +#include "php_dio_common.h" ++#include "php_dio_stream_wrappers.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 ++/* ++ +----------------------------------------------------------------------+ ++ | Raw stream handling | ++ +----------------------------------------------------------------------+ ++*/ + -+ return flags; ++/* {{{ dio_stream_write ++ * Write to the stream ++ */ ++static size_t dio_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) ++{ ++ return dio_common_write((php_dio_stream_data*)stream->abstract, buf, count); +} +/* }}} */ + -+/* {{{ dio_data_rate_to_define -+ * Converts a numeric data rate to a termios define ++/* {{{ dio_stream_read ++ * Read from the stream + */ -+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; -+ } ++static size_t dio_stream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) ++{ ++ php_dio_stream_data* data = (php_dio_stream_data*)stream->abstract; ++ size_t bytes = dio_common_read(data, buf, count); ++ stream->eof = data->end_of_file; + -+ *def = val; -+ return 1; ++ return bytes; +} ++/* }}} */ + -+/* {{{ dio_data_bits_to_define -+ * Converts a number of data bits to a termios define ++/* {{{ dio_stream_flush ++ * Flush the stream. For raw streams this does nothing. + */ -+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; ++static int dio_stream_flush(php_stream *stream TSRMLS_DC) ++{ + return 1; +} +/* }}} */ + -+/* {{{ dio_stop_bits_to_define -+ * Converts a number of stop bits to a termios define ++/* {{{ dio_stream_close ++ * Close the stream + */ -+static int dio_stop_bits_to_define(int stop_bits, int *def) { -+ int val; ++static int dio_stream_close(php_stream *stream, int close_handle TSRMLS_DC) ++{ ++ php_dio_stream_data *abstract = (php_dio_stream_data*)stream->abstract; + -+ switch (stop_bits) { -+ case 1: -+ val = 0; -+ break; -+ case 2: -+ val = CSTOPB; -+ break; -+ default: -+ return 0; ++ if (!dio_common_close(abstract)) { ++ return 0; + } + -+ *def = val; ++ efree(abstract); + return 1; +} +/* }}} */ + -+/* {{{ dio_parity_to_define -+ * Converts a parity type to a termios define ++/* {{{ dio_stream_set_option ++ * Set the stream options. + */ -+static int dio_parity_to_define(int parity, int *def) { -+ int val; ++static int dio_stream_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) ++{ ++ php_dio_stream_data *abstract = (php_dio_stream_data*)stream->abstract; + -+ switch (parity) { -+ case 0: -+ val = 0; -+ break; -+ case 1: -+ val = PARENB | PARODD; -+ break; -+ case 2: -+ val = PARENB; -+ break; -+ default: -+ return 0; -+ } ++ switch (option) { ++ case PHP_STREAM_OPTION_META_DATA_API: ++#ifdef DIO_NONBLOCK ++ add_assoc_bool((zval *)ptrparam, "timed_out", abstract->timed_out); ++ add_assoc_bool((zval *)ptrparam, "blocked", abstract->is_blocking); ++#endif ++ add_assoc_bool((zval *)ptrparam, "eof", stream->eof); ++ return PHP_STREAM_OPTION_RETURN_OK; + -+ *def = val; -+ return 1; -+} -+/* }}} */ ++#if PHP_MAJOR_VERSION >= 5 ++ case PHP_STREAM_OPTION_CHECK_LIVENESS: ++ stream->eof = abstract->end_of_file; ++ return PHP_STREAM_OPTION_RETURN_OK; ++#endif /* PHP_MAJOR_VERSION >= 5 */ + -+/* {{{ 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; ++ default: ++ break; ++ } + -+ return (php_dio_stream_data *)data; ++ return dio_common_set_option(abstract, option, value, ptrparam); +} +/* }}} */ + -+/* {{{ 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; -+} -+/* }}} */ ++php_stream_ops dio_raw_stream_ops = { ++ dio_stream_write, ++ dio_stream_read, ++ dio_stream_close, ++ dio_stream_flush, ++ "dio", ++ NULL, /* seek */ ++ NULL, /* cast */ ++ NULL, /* stat */ ++ dio_stream_set_option, ++}; + -+#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. ++/* {{{ dio_raw_fopen_wrapper ++ * fopen for the dio.raw stream. + */ -+static int dio_timeval_subtract(struct timeval *late, struct timeval *early, struct timeval *diff) { -+ struct timeval *tmp; ++static php_stream *dio_raw_fopen_wrapper(php_stream_wrapper *wrapper, ++ char *path, char *mode, ++ int options, char **opened_path, ++ php_stream_context *context STREAMS_DC TSRMLS_DC) { ++ php_dio_stream_data *data; ++ php_stream *stream; ++ char *filename; + -+ /* Handle negatives */ -+ if (late->tv_sec < early->tv_sec) { -+ return 0; ++ /* Check it was actually for us (not a corrupted function pointer ++ somewhere!). */ ++ if (strncmp(path, DIO_RAW_STREAM_PROTOCOL, sizeof(DIO_RAW_STREAM_PROTOCOL) - 1)) { ++ return NULL; + } + -+ if ((late->tv_sec == early->tv_sec) && (late->tv_usec < early->tv_usec)) { -+ return 0; -+ } ++ /* Get the actually file system name/path. */ ++ filename = path + sizeof(DIO_RAW_STREAM_PROTOCOL) - 1; + -+ /* 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--; ++ /* Check we can actually access it. */ ++ if (php_check_open_basedir(filename TSRMLS_CC) || DIO_SAFE_MODE_CHECK(filename, mode)) { ++ return NULL; + } + -+ /* Once adjusted can just subtract values. */ -+ diff->tv_sec = late->tv_sec - early->tv_sec; -+ diff->tv_usec = late->tv_usec - early->tv_usec; ++ data = dio_create_stream_data(); ++ data->stream_type = DIO_STREAM_TYPE_RAW; + -+ return 1; -+} -+#endif ++ /* Parse the context. */ ++ if (context) { ++ dio_stream_context_get_basic_options(context, data TSRMLS_CC); ++ } + -+/* {{{ 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; ++ /* Try and open a raw stream. */ ++ if (!dio_raw_open_stream(filename, mode, data TSRMLS_CC)) { ++ return NULL; + } -+#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 -+ */ ---- /dev/null -+++ b/ext/dio/dio_stream_wrappers.c -@@ -0,0 +1,410 @@ -+/* -+ +----------------------------------------------------------------------+ -+ | 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 | -+ +----------------------------------------------------------------------+ -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include "php.h" -+#include "ext/standard/url.h" -+ -+#include "php_dio.h" -+#include "php_dio_common.h" -+#include "php_dio_stream_wrappers.h" -+ -+/* -+ +----------------------------------------------------------------------+ -+ | Raw stream handling | -+ +----------------------------------------------------------------------+ -+*/ -+ -+/* {{{ dio_stream_write -+ * Write to the stream -+ */ -+static size_t dio_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) -+{ -+ return dio_common_write((php_dio_stream_data*)stream->abstract, buf, count); -+} -+/* }}} */ -+ -+/* {{{ dio_stream_read -+ * Read from the stream -+ */ -+static size_t dio_stream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) -+{ -+ php_dio_stream_data* data = (php_dio_stream_data*)stream->abstract; -+ size_t bytes = dio_common_read(data, buf, count); -+ stream->eof = data->end_of_file; -+ -+ return bytes; -+} -+/* }}} */ -+ -+/* {{{ dio_stream_flush -+ * Flush the stream. For raw streams this does nothing. -+ */ -+static int dio_stream_flush(php_stream *stream TSRMLS_DC) -+{ -+ return 1; -+} -+/* }}} */ -+ -+/* {{{ dio_stream_close -+ * Close the stream -+ */ -+static int dio_stream_close(php_stream *stream, int close_handle TSRMLS_DC) -+{ -+ php_dio_stream_data *abstract = (php_dio_stream_data*)stream->abstract; -+ -+ if (!dio_common_close(abstract)) { -+ return 0; -+ } -+ -+ efree(abstract); -+ return 1; -+} -+/* }}} */ -+ -+/* {{{ dio_stream_set_option -+ * Set the stream options. -+ */ -+static int dio_stream_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) -+{ -+ php_dio_stream_data *abstract = (php_dio_stream_data*)stream->abstract; -+ -+ switch (option) { -+ case PHP_STREAM_OPTION_META_DATA_API: -+#ifdef DIO_NONBLOCK -+ add_assoc_bool((zval *)ptrparam, "timed_out", abstract->timed_out); -+ add_assoc_bool((zval *)ptrparam, "blocked", abstract->is_blocking); -+#endif -+ add_assoc_bool((zval *)ptrparam, "eof", stream->eof); -+ return PHP_STREAM_OPTION_RETURN_OK; -+ -+#if PHP_MAJOR_VERSION >= 5 -+ case PHP_STREAM_OPTION_CHECK_LIVENESS: -+ stream->eof = abstract->end_of_file; -+ return PHP_STREAM_OPTION_RETURN_OK; -+#endif /* PHP_MAJOR_VERSION >= 5 */ -+ -+ default: -+ break; -+ } -+ -+ return dio_common_set_option(abstract, option, value, ptrparam); -+} -+/* }}} */ -+ -+php_stream_ops dio_raw_stream_ops = { -+ dio_stream_write, -+ dio_stream_read, -+ dio_stream_close, -+ dio_stream_flush, -+ "dio", -+ NULL, /* seek */ -+ NULL, /* cast */ -+ NULL, /* stat */ -+ dio_stream_set_option, -+}; -+ -+/* {{{ dio_raw_fopen_wrapper -+ * fopen for the dio.raw stream. -+ */ -+static php_stream *dio_raw_fopen_wrapper(php_stream_wrapper *wrapper, -+ char *path, char *mode, -+ int options, char **opened_path, -+ php_stream_context *context STREAMS_DC TSRMLS_DC) { -+ php_dio_stream_data *data; -+ php_stream *stream; -+ char *filename; -+ -+ /* Check it was actually for us (not a corrupted function pointer -+ somewhere!). */ -+ if (strncmp(path, DIO_RAW_STREAM_PROTOCOL, sizeof(DIO_RAW_STREAM_PROTOCOL) - 1)) { -+ return NULL; -+ } -+ -+ /* Get the actually file system name/path. */ -+ filename = path + sizeof(DIO_RAW_STREAM_PROTOCOL) - 1; -+ -+ /* Check we can actually access it. */ -+ if (php_check_open_basedir(filename TSRMLS_CC) || -+ (PG(safe_mode) && !php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) { -+ return NULL; -+ } -+ -+ data = dio_create_stream_data(); -+ data->stream_type = DIO_STREAM_TYPE_RAW; -+ -+ /* Parse the context. */ -+ if (context) { -+ dio_stream_context_get_basic_options(context, data TSRMLS_CC); -+ } -+ -+ /* Try and open a raw stream. */ -+ if (!dio_raw_open_stream(filename, mode, data TSRMLS_CC)) { -+ return NULL; -+ } -+ -+ /* Create a PHP stream based on raw stream */ -+ stream = php_stream_alloc(&dio_raw_stream_ops, data, 0, mode); -+ if (!stream) { -+ (void) dio_common_close(data); -+ efree(data); -+ } -+ -+ return stream; -+} -+/* }}} */ -+ -+static php_stream_wrapper_ops dio_raw_stream_wops = { -+ dio_raw_fopen_wrapper, -+ NULL, /* stream_close */ -+ NULL, /* stat */ -+ NULL, /* stat_url */ -+ NULL, /* opendir */ -+ DIO_RAW_STREAM_NAME -+}; -+ -+php_stream_wrapper php_dio_raw_stream_wrapper = { -+ &dio_raw_stream_wops, -+ NULL, -+ 0 -+}; -+ -+/* {{{ proto dio_raw(string filename, string mode[, array options]) -+ * Opens a raw direct IO stream. -+ */ -+PHP_FUNCTION(dio_raw) { -+ zval *options = NULL; -+ php_dio_stream_data *data; -+ php_stream *stream; -+ -+ char *filename; -+ int filename_len; -+ char *mode; -+ int mode_len; -+ -+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|z", &filename, &filename_len, &mode, &mode_len, &options) == FAILURE) { -+ RETURN_FALSE; -+ } -+ -+ /* Check the third argument is an array. */ -+ if (options && (Z_TYPE_P(options) != IS_ARRAY)) { -+ RETURN_FALSE; -+ } -+ -+ /* Check we can actually access the file. */ -+ if (php_check_open_basedir(filename TSRMLS_CC) || -+ (PG(safe_mode) && !php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) { -+ RETURN_FALSE; -+ } -+ -+ data = dio_create_stream_data(); -+ data->stream_type = DIO_STREAM_TYPE_RAW; -+ -+ if (options) { -+ dio_assoc_array_get_basic_options(options, data TSRMLS_CC); -+ } -+ -+ /* Try and open a raw stream. */ -+ if (dio_raw_open_stream(filename, mode, data TSRMLS_CC)) { -+ stream = php_stream_alloc(&dio_raw_stream_ops, data, 0, mode); -+ if (!stream) { -+ (void) dio_common_close(data); -+ efree(data); -+ RETURN_FALSE; -+ } -+ } -+ -+ php_stream_to_zval(stream, return_value); -+} -+/* }}} */ -+ -+/* -+ +----------------------------------------------------------------------+ -+ | Serial stream handling | -+ +----------------------------------------------------------------------+ -+*/ -+ -+/* {{{ dio_stream_flush -+ * Flush the stream. If the stream is read only, it flushes the read -+ * stream, if it is write only it flushes the write, otherwise it flushes -+ * both. -+ */ -+static int dio_serial_stream_flush(php_stream *stream TSRMLS_DC) -+{ -+ return dio_serial_purge((php_dio_stream_data*)stream->abstract); -+} -+/* }}} */ -+ -+/* {{{ dio_stream_close -+ * Close the stream. Restores the serial settings to their value before -+ * the stream was open. -+ */ -+static int dio_serial_stream_close(php_stream *stream, int close_handle TSRMLS_DC) -+{ -+ php_dio_stream_data *abstract = (php_dio_stream_data*)stream->abstract; -+ -+ if (!dio_serial_uninit(abstract)) { -+ return 0; -+ } -+ -+ if (!dio_common_close(abstract)) { -+ return 0; -+ } -+ -+ efree(abstract); -+ return 1; -+} -+/* }}} */ -+ -+php_stream_ops dio_serial_stream_ops = { -+ dio_stream_write, -+ dio_stream_read, -+ dio_serial_stream_close, -+ dio_serial_stream_flush, -+ "dio", -+ NULL, /* seek */ -+ NULL, /* cast */ -+ NULL, /* stat */ -+ dio_stream_set_option, -+}; -+ -+/* {{{ dio_raw_fopen_wrapper -+ * fopen for the dio.raw stream. -+ */ -+static php_stream *dio_serial_fopen_wrapper(php_stream_wrapper *wrapper, -+ char *path, char *mode, -+ int options, char **opened_path, -+ php_stream_context *context STREAMS_DC TSRMLS_DC) { -+ php_dio_stream_data *data; -+ php_stream *stream; -+ char *filename; -+ -+ /* Check it was actually for us (not a corrupted function pointer -+ somewhere!). */ -+ if (strncmp(path, DIO_SERIAL_STREAM_PROTOCOL, sizeof(DIO_SERIAL_STREAM_PROTOCOL) - 1)) { -+ return NULL; -+ } -+ -+ /* Get the actually file system name/path. */ -+ filename = path + sizeof(DIO_SERIAL_STREAM_PROTOCOL) - 1; -+ -+ /* Check we can actually access it. */ -+ if (php_check_open_basedir(filename TSRMLS_CC) || -+ (PG(safe_mode) && !php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) { -+ return NULL; -+ } -+ -+ data = dio_create_stream_data(); -+ data->stream_type = DIO_STREAM_TYPE_SERIAL; -+ -+ /* Parse the context. */ -+ if (context) { -+ dio_stream_context_get_basic_options(context, data TSRMLS_CC); -+ dio_stream_context_get_serial_options(context, data TSRMLS_CC); -+ } -+ -+ /* Try and open a serial stream. */ -+ if (!dio_serial_open_stream(filename, mode, data TSRMLS_CC)) { -+ return NULL; -+ } -+ -+ stream = php_stream_alloc(&dio_serial_stream_ops, data, 0, mode); -+ if (!stream) { -+ efree(data); -+ } ++ /* Create a PHP stream based on raw stream */ ++ stream = php_stream_alloc(&dio_raw_stream_ops, data, 0, mode); ++ if (!stream) { ++ (void) dio_common_close(data); ++ efree(data); ++ } + + return stream; +} +/* }}} */ + -+static php_stream_wrapper_ops dio_serial_stream_wops = { -+ dio_serial_fopen_wrapper, ++static php_stream_wrapper_ops dio_raw_stream_wops = { ++ dio_raw_fopen_wrapper, + NULL, /* stream_close */ + NULL, /* stat */ + NULL, /* stat_url */ + NULL, /* opendir */ -+ DIO_SERIAL_STREAM_NAME ++ DIO_RAW_STREAM_NAME +}; + -+php_stream_wrapper php_dio_serial_stream_wrapper = { -+ &dio_serial_stream_wops, ++php_stream_wrapper php_dio_raw_stream_wrapper = { ++ &dio_raw_stream_wops, + NULL, + 0 +}; + -+/* {{{ proto dio_serial(string filename, string mode[, array options]) -+ * Opens a serial direct IO stream. ++/* {{{ proto dio_raw(string filename, string mode[, array options]) ++ * Opens a raw direct IO stream. + */ -+PHP_FUNCTION(dio_serial) { ++PHP_FUNCTION(dio_raw) { + zval *options = NULL; + php_dio_stream_data *data; + php_stream *stream; @@ -2084,706 +1990,194 @@ + + /* Check the third argument is an array. */ + if (options && (Z_TYPE_P(options) != IS_ARRAY)) { -+ php_error_docref(NULL TSRMLS_CC, E_WARNING,"dio_serial, the third argument should be an array of options"); + RETURN_FALSE; + } + + /* Check we can actually access the file. */ -+ if (php_check_open_basedir(filename TSRMLS_CC) || -+ (PG(safe_mode) && !php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) { ++ if (php_check_open_basedir(filename TSRMLS_CC) || DIO_SAFE_MODE_CHECK(filename, mode)) { + RETURN_FALSE; + } + + data = dio_create_stream_data(); -+ data->stream_type = DIO_STREAM_TYPE_SERIAL; ++ data->stream_type = DIO_STREAM_TYPE_RAW; + + if (options) { + dio_assoc_array_get_basic_options(options, data TSRMLS_CC); -+ dio_assoc_array_get_serial_options(options, data TSRMLS_CC); + } + -+ /* Try and open a serial stream. */ -+ if (dio_serial_open_stream(filename, mode, data TSRMLS_CC)) { -+ stream = php_stream_alloc(&dio_serial_stream_ops, data, 0, mode); ++ /* Try and open a raw stream. */ ++ if (dio_raw_open_stream(filename, mode, data TSRMLS_CC)) { ++ stream = php_stream_alloc(&dio_raw_stream_ops, data, 0, mode); + if (!stream) { -+ efree(data); -+ RETURN_FALSE; -+ } -+ } -+ -+ php_stream_to_zval(stream, return_value); -+} -+/* }}} */ -+ -+/* -+ * Local variables: -+ * c-basic-offset: 4 -+ * tab-width: 4 -+ * End: -+ * vim600: fdm=marker -+ * vim: sw=4 ts=4 noet -+ */ ---- /dev/null -+++ b/ext/dio/dio_win32.c -@@ -0,0 +1,669 @@ -+/* -+ +----------------------------------------------------------------------+ -+ | 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 | -+ +----------------------------------------------------------------------+ -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include "php.h" -+ -+#include "php_dio_common.h" -+ -+/* {{{ 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: -+ 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 = 0; -+ break; -+ case 2: -+ val = 2; -+ break; -+ case 3: -+ val = 1; -+ 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) { -+ switch (parity) { -+ case 0: -+ case 1: -+ case 2: -+ break; -+ default: -+ return 0; -+ } -+ -+ *def = (DWORD)parity; -+ 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; ++ (void) dio_common_close(data); ++ efree(data); ++ RETURN_FALSE; + } + } + -+ return 1; ++ php_stream_to_zval(stream, return_value); +} +/* }}} */ + -+/* {{{ 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 ++; ++/* ++ +----------------------------------------------------------------------+ ++ | Serial stream handling | ++ +----------------------------------------------------------------------+ ++*/ + -+ if (*mode && (*mode != '+')) { -+ mode++; -+ } ++/* {{{ dio_stream_flush ++ * Flush the stream. If the stream is read only, it flushes the read ++ * stream, if it is write only it flushes the write, otherwise it flushes ++ * both. ++ */ ++static int dio_serial_stream_flush(php_stream *stream TSRMLS_DC) ++{ ++ return dio_serial_purge((php_dio_stream_data*)stream->abstract); ++} ++/* }}} */ + -+ 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) { -+ wdata->handle = CreateFile(filename, wdata->desired_access, 0, -+ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); -+ if (INVALID_HANDLE_VALUE == wdata->handle) { -+ err = GetLastError(); -+ return 0; -+ } -+ } else { -+ return 0; -+ } -+ break; ++/* {{{ dio_stream_close ++ * Close the stream. Restores the serial settings to their value before ++ * the stream was open. ++ */ ++static int dio_serial_stream_close(php_stream *stream, int close_handle TSRMLS_DC) ++{ ++ php_dio_stream_data *abstract = (php_dio_stream_data*)stream->abstract; + -+ default: -+ return 0; -+ } ++ if (!dio_serial_uninit(abstract)) { ++ 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; ++ if (!dio_common_close(abstract)) { ++ return 0; + } + ++ efree(abstract); + return 1; +} +/* }}} */ + -+/* {{{ dio_serial_init -+ * Initialises the serial port ++php_stream_ops dio_serial_stream_ops = { ++ dio_stream_write, ++ dio_stream_read, ++ dio_serial_stream_close, ++ dio_serial_stream_flush, ++ "dio", ++ NULL, /* seek */ ++ NULL, /* cast */ ++ NULL, /* stat */ ++ dio_stream_set_option, ++}; ++ ++/* {{{ dio_raw_fopen_wrapper ++ * fopen for the dio.raw stream. + */ -+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 err, rate_def, data_bits_def, stop_bits_def, parity_def; -+ DCB dcb; ++static php_stream *dio_serial_fopen_wrapper(php_stream_wrapper *wrapper, ++ char *path, char *mode, ++ int options, char **opened_path, ++ php_stream_context *context STREAMS_DC TSRMLS_DC) { ++ php_dio_stream_data *data; ++ php_stream *stream; ++ char *filename; + -+ if (!dio_data_rate_to_define(data->data_rate, &rate_def)) { -+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid data_rate value (%d) (%d)", data->data_rate, __LINE__); -+ return 0; ++ /* Check it was actually for us (not a corrupted function pointer ++ somewhere!). */ ++ if (strncmp(path, DIO_SERIAL_STREAM_PROTOCOL, sizeof(DIO_SERIAL_STREAM_PROTOCOL) - 1)) { ++ return NULL; + } + -+ 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; -+ } ++ /* Get the actually file system name/path. */ ++ filename = path + sizeof(DIO_SERIAL_STREAM_PROTOCOL) - 1; + -+ 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; ++ /* Check we can actually access it. */ ++ if (php_check_open_basedir(filename TSRMLS_CC) || DIO_SAFE_MODE_CHECK(filename, mode)) { ++ return NULL; + } + -+ 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; -+ } ++ data = dio_create_stream_data(); ++ data->stream_type = DIO_STREAM_TYPE_SERIAL; + -+ if (!GetCommState(wdata->handle, &(wdata->olddcb))) { -+ err = GetLastError(); -+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "GetCommState() failed! (%d)", err); -+ return 0; ++ /* Parse the context. */ ++ if (context) { ++ dio_stream_context_get_basic_options(context, data TSRMLS_CC); ++ dio_stream_context_get_serial_options(context, data TSRMLS_CC); + } + -+ /* 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; ++ /* Try and open a serial stream. */ ++ if (!dio_serial_open_stream(filename, mode, data TSRMLS_CC)) { ++ return NULL; + } + -+ if (!SetCommState(wdata->handle, &dcb)) { -+ return 0; ++ stream = php_stream_alloc(&dio_serial_stream_ops, data, 0, mode); ++ if (!stream) { ++ efree(data); + } + -+ return 1; ++ return stream; +} +/* }}} */ + ++static php_stream_wrapper_ops dio_serial_stream_wops = { ++ dio_serial_fopen_wrapper, ++ NULL, /* stream_close */ ++ NULL, /* stat */ ++ NULL, /* stat_url */ ++ NULL, /* opendir */ ++ DIO_SERIAL_STREAM_NAME ++}; + -+/* {{{ 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; -+} -+/* }}} */ ++php_stream_wrapper php_dio_serial_stream_wrapper = { ++ &dio_serial_stream_wops, ++ NULL, ++ 0 ++}; + -+/* {{{ dio_serial_flush -+ * Purges the serial buffers of data. ++/* {{{ proto dio_serial(string filename, string mode[, array options]) ++ * Opens a serial direct IO stream. + */ -+int dio_serial_purge(php_dio_stream_data *data) { -+ php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data; -+ BOOL ret; ++PHP_FUNCTION(dio_serial) { ++ zval *options = NULL; ++ php_dio_stream_data *data; ++ php_stream *stream; + -+ /* 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; -+ } ++ char *filename; ++ int filename_len; ++ char *mode; ++ int mode_len; + -+ /* 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); ++ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|z", &filename, &filename_len, &mode, &mode_len, &options) == FAILURE) { ++ RETURN_FALSE; + } + -+ 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 }; -+ DWORD err; -+ -+ if (!dio_raw_open_stream(filename, mode, data TSRMLS_CC)) { -+ return 0; ++ /* Check the third argument is an array. */ ++ if (options && (Z_TYPE_P(options) != IS_ARRAY)) { ++ php_error_docref(NULL TSRMLS_CC, E_WARNING,"dio_serial, the third argument should be an array of options"); ++ RETURN_FALSE; + } + -+ if (!GetCommTimeouts(wdata->handle, &(wdata->oldcto))) { -+ err = GetLastError(); -+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "SetCommTimeouts() failed! (%d) Not a comm port?", err); -+ CloseHandle(wdata->handle); -+ return 0; ++ /* Check we can actually access the file. */ ++ if (php_check_open_basedir(filename TSRMLS_CC) || DIO_SAFE_MODE_CHECK(filename, mode)) { ++ RETURN_FALSE; + } + -+ /* 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); -+ } ++ data = dio_create_stream_data(); ++ data->stream_type = DIO_STREAM_TYPE_SERIAL; + -+ if (!SetCommTimeouts(wdata->handle, &cto)) { -+ CloseHandle(wdata->handle); -+ return 0; ++ if (options) { ++ dio_assoc_array_get_basic_options(options, data TSRMLS_CC); ++ dio_assoc_array_get_serial_options(options, data TSRMLS_CC); + } + -+ if (!dio_serial_init(data TSRMLS_CC)) { -+ CloseHandle(wdata->handle); -+ return 0; ++ /* Try and open a serial stream. */ ++ if (dio_serial_open_stream(filename, mode, data TSRMLS_CC)) { ++ stream = php_stream_alloc(&dio_serial_stream_ops, data, 0, mode); ++ if (!stream) { ++ efree(data); ++ RETURN_FALSE; ++ } + } + -+ return 1; ++ php_stream_to_zval(stream, return_value); +} +/* }}} */ + @@ -2796,6 +2190,678 @@ + * vim: sw=4 ts=4 noet + */ --- /dev/null ++++ b/ext/dio/dio_win32.c +@@ -0,0 +1,669 @@ ++/* ++ +----------------------------------------------------------------------+ ++ | 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 | ++ +----------------------------------------------------------------------+ ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif ++ ++#include "php.h" ++ ++#include "php_dio_common.h" ++ ++/* {{{ 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: ++ 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 = 0; ++ break; ++ case 2: ++ val = 2; ++ break; ++ case 3: ++ val = 1; ++ 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) { ++ switch (parity) { ++ case 0: ++ case 1: ++ case 2: ++ break; ++ default: ++ return 0; ++ } ++ ++ *def = (DWORD)parity; ++ 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) { ++ wdata->handle = CreateFile(filename, wdata->desired_access, 0, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (INVALID_HANDLE_VALUE == wdata->handle) { ++ err = GetLastError(); ++ return 0; ++ } ++ } else { ++ return 0; ++ } ++ break; ++ ++ default: ++ 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 err, 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) (%d)", data->data_rate, __LINE__); ++ 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))) { ++ err = GetLastError(); ++ php_error_docref(NULL TSRMLS_CC, E_WARNING, "GetCommState() failed! (%d)", err); ++ 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)) { ++ 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 }; ++ DWORD err; ++ ++ if (!dio_raw_open_stream(filename, mode, data TSRMLS_CC)) { ++ return 0; ++ } ++ ++ if (!GetCommTimeouts(wdata->handle, &(wdata->oldcto))) { ++ err = GetLastError(); ++ php_error_docref(NULL TSRMLS_CC, E_WARNING, "SetCommTimeouts() failed! (%d) Not a comm port?", err); ++ 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)) { ++ 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 ++ */ +--- /dev/null +++ b/ext/dio/php_dio.h @@ -0,0 +1,60 @@ +/* @@ -3003,79 +3069,79 @@ --- /dev/null +++ b/ext/dio/php_dio_posix.h @@ -0,0 +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 | -+ +----------------------------------------------------------------------+ -+ */ -+ -+#ifndef PHP_DIO_POSIX_H_ -+#define PHP_DIO_POSIX_H_ -+ -+#include -+#include -+#include -+#include -+ -+#ifdef HAVE_UNISTD_H -+#include -+#endif -+ -+#include -+#include -+ -+ -+/** ++/* ++ +----------------------------------------------------------------------+ ++ | 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 | ++ +----------------------------------------------------------------------+ ++ */ ++ ++#ifndef PHP_DIO_POSIX_H_ ++#define PHP_DIO_POSIX_H_ ++ ++#include ++#include ++#include ++#include ++ ++#ifdef HAVE_UNISTD_H ++#include ++#endif ++ ++#include ++#include ++ ++ ++/** + * 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 ++ */ --- /dev/null +++ b/ext/dio/php_dio_stream_wrappers.h -@@ -0,0 +1,44 @@ +@@ -0,0 +1,51 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | @@ -3102,6 +3168,13 @@ +#define DIO_SERIAL_STREAM_NAME "dio.serial" +#define DIO_SERIAL_STREAM_PROTOCOL "dio.serial://" + ++/* To support PHP 5.4 and later */ ++#if PHP_VERSION_ID < 50399 ++#define DIO_SAFE_MODE_CHECK(f, m) (PG(safe_mode) && !php_checkuid(f, m, CHECKUID_CHECK_MODE_PARAM)) ++#else ++#define DIO_SAFE_MODE_CHECK(f, m) (0) ++#endif ++ +extern php_stream_wrapper php_dio_raw_stream_wrapper; + +PHP_FUNCTION(dio_raw); @@ -3123,68 +3196,68 @@ --- /dev/null +++ b/ext/dio/php_dio_win32.h @@ -0,0 +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 | -+ +----------------------------------------------------------------------+ -+ */ -+ -+#ifndef PHP_DIO_WIN32_H_ -+#define PHP_DIO_WIN32_H_ -+ -+#include -+ -+/* 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 | ++ +----------------------------------------------------------------------+ ++ */ ++ ++#ifndef PHP_DIO_WIN32_H_ ++#define PHP_DIO_WIN32_H_ ++ ++#include ++ ++/* 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 ++ */ --- /dev/null +++ b/ext/dio/tests/001.phpt @@ -0,0 +1,23 @@ diff --git a/lang/php5/patches/011-PECL-dio-additional-baudrates.patch b/lang/php5/patches/011-PECL-dio-additional-baudrates.patch index 11a298179a..fc534d022c 100644 --- a/lang/php5/patches/011-PECL-dio-additional-baudrates.patch +++ b/lang/php5/patches/011-PECL-dio-additional-baudrates.patch @@ -1,6 +1,6 @@ --- a/ext/dio/dio.c +++ b/ext/dio/dio.c -@@ -442,6 +442,26 @@ PHP_FUNCTION(dio_tcsetattr) +@@ -501,6 +501,26 @@ PHP_FUNCTION(dio_tcsetattr) /* assign to correct values... */ switch (Baud_Rate) {