diff options
-rw-r--r-- | gl/accept.c | 42 | ||||
-rw-r--r-- | gl/alloca.c | 489 | ||||
-rw-r--r-- | gl/bind.c | 40 | ||||
-rw-r--r-- | gl/close.c | 82 | ||||
-rw-r--r-- | gl/fclose.c | 47 | ||||
-rw-r--r-- | gl/listen.c | 40 | ||||
-rw-r--r-- | gl/m4/close.m4 | 25 | ||||
-rw-r--r-- | gl/m4/fclose.m4 | 18 | ||||
-rw-r--r-- | gl/m4/perror.m4 | 17 | ||||
-rw-r--r-- | gl/m4/sockets.m4 | 65 | ||||
-rw-r--r-- | gl/m4/sys_ioctl_h.m4 | 60 | ||||
-rw-r--r-- | gl/m4/sys_select_h.m4 | 45 | ||||
-rw-r--r-- | gl/m4/sys_time_h.m4 | 57 | ||||
-rw-r--r-- | gl/setsockopt.c | 40 | ||||
-rw-r--r-- | gl/socket.c | 43 | ||||
-rw-r--r-- | gl/sys_select.in.h | 78 | ||||
-rw-r--r-- | gl/sys_time.in.h | 56 | ||||
-rw-r--r-- | gl/tests/connect.c | 47 | ||||
-rw-r--r-- | gl/tests/ioctl.c | 49 | ||||
-rw-r--r-- | gl/tests/perror.c | 35 | ||||
-rw-r--r-- | gl/tests/sockets.c | 57 | ||||
-rw-r--r-- | gl/tests/sockets.h | 32 | ||||
-rw-r--r-- | gl/tests/sys_ioctl.in.h | 70 | ||||
-rw-r--r-- | gl/tests/test-perror.c | 34 | ||||
-rwxr-xr-x | gl/tests/test-perror.sh | 27 | ||||
-rw-r--r-- | gl/tests/test-select.c | 376 | ||||
-rw-r--r-- | gl/tests/test-sockets.c | 44 | ||||
-rw-r--r-- | gl/tests/test-sys_select.c | 30 | ||||
-rw-r--r-- | gl/tests/test-sys_time.c | 29 | ||||
-rw-r--r-- | gl/tests/w32sock.h | 62 | ||||
-rw-r--r-- | gl/winsock-select.c | 423 |
31 files changed, 2559 insertions, 0 deletions
diff --git a/gl/accept.c b/gl/accept.c new file mode 100644 index 0000000000..ae46537792 --- /dev/null +++ b/gl/accept.c @@ -0,0 +1,42 @@ +/* accept.c --- wrappers for Windows accept function + + Copyright (C) 2008 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Paolo Bonzini */ + +#include <config.h> + +#define WIN32_LEAN_AND_MEAN +/* Get winsock2.h. */ +#include <sys/socket.h> + +/* Get set_winsock_errno, FD_TO_SOCKET etc. */ +#include "w32sock.h" + +#undef accept + +int +rpl_accept (int fd, struct sockaddr *addr, int *addrlen) +{ + SOCKET fh = accept (FD_TO_SOCKET (fd), addr, addrlen); + if (fh == INVALID_SOCKET) + { + set_winsock_errno (); + return -1; + } + else + return SOCKET_TO_FD (fh); +} diff --git a/gl/alloca.c b/gl/alloca.c new file mode 100644 index 0000000000..ff1cb7e9a6 --- /dev/null +++ b/gl/alloca.c @@ -0,0 +1,489 @@ +/* alloca.c -- allocate automatically reclaimed memory + (Mostly) portable public-domain implementation -- D A Gwyn + + This implementation of the PWB library alloca function, + which is used to allocate space off the run-time stack so + that it is automatically reclaimed upon procedure exit, + was inspired by discussions with J. Q. Johnson of Cornell. + J.Otto Tennant <jot@cray.com> contributed the Cray support. + + There are some preprocessor constants that can + be defined when compiling for your specific system, for + improved efficiency; however, the defaults should be okay. + + The general concept of this implementation is to keep + track of all alloca-allocated blocks, and reclaim any + that are found to be deeper in the stack than the current + invocation. This heuristic does not reclaim storage as + soon as it becomes invalid, but it will do so eventually. + + As a special case, alloca(0) reclaims storage without + allocating any. It is a good idea to use alloca(0) in + your main control loop, etc. to force garbage collection. */ + +#include <config.h> + +#include <alloca.h> + +#include <string.h> +#include <stdlib.h> + +#ifdef emacs +# include "lisp.h" +# include "blockinput.h" +# ifdef EMACS_FREE +# undef free +# define free EMACS_FREE +# endif +#else +# define memory_full() abort () +#endif + +/* If compiling with GCC 2, this file's not needed. */ +#if !defined (__GNUC__) || __GNUC__ < 2 + +/* If someone has defined alloca as a macro, + there must be some other way alloca is supposed to work. */ +# ifndef alloca + +# ifdef emacs +# ifdef static +/* actually, only want this if static is defined as "" + -- this is for usg, in which emacs must undefine static + in order to make unexec workable + */ +# ifndef STACK_DIRECTION +you +lose +-- must know STACK_DIRECTION at compile-time +/* Using #error here is not wise since this file should work for + old and obscure compilers. */ +# endif /* STACK_DIRECTION undefined */ +# endif /* static */ +# endif /* emacs */ + +/* If your stack is a linked list of frames, you have to + provide an "address metric" ADDRESS_FUNCTION macro. */ + +# if defined (CRAY) && defined (CRAY_STACKSEG_END) +long i00afunc (); +# define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg)) +# else +# define ADDRESS_FUNCTION(arg) &(arg) +# endif + +/* Define STACK_DIRECTION if you know the direction of stack + growth for your system; otherwise it will be automatically + deduced at run-time. + + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ + +# ifndef STACK_DIRECTION +# define STACK_DIRECTION 0 /* Direction unknown. */ +# endif + +# if STACK_DIRECTION != 0 + +# define STACK_DIR STACK_DIRECTION /* Known at compile-time. */ + +# else /* STACK_DIRECTION == 0; need run-time code. */ + +static int stack_dir; /* 1 or -1 once known. */ +# define STACK_DIR stack_dir + +static void +find_stack_direction (void) +{ + static char *addr = NULL; /* Address of first `dummy', once known. */ + auto char dummy; /* To get stack address. */ + + if (addr == NULL) + { /* Initial entry. */ + addr = ADDRESS_FUNCTION (dummy); + + find_stack_direction (); /* Recurse once. */ + } + else + { + /* Second entry. */ + if (ADDRESS_FUNCTION (dummy) > addr) + stack_dir = 1; /* Stack grew upward. */ + else + stack_dir = -1; /* Stack grew downward. */ + } +} + +# endif /* STACK_DIRECTION == 0 */ + +/* An "alloca header" is used to: + (a) chain together all alloca'ed blocks; + (b) keep track of stack depth. + + It is very important that sizeof(header) agree with malloc + alignment chunk size. The following default should work okay. */ + +# ifndef ALIGN_SIZE +# define ALIGN_SIZE sizeof(double) +# endif + +typedef union hdr +{ + char align[ALIGN_SIZE]; /* To force sizeof(header). */ + struct + { + union hdr *next; /* For chaining headers. */ + char *deep; /* For stack depth measure. */ + } h; +} header; + +static header *last_alloca_header = NULL; /* -> last alloca header. */ + +/* Return a pointer to at least SIZE bytes of storage, + which will be automatically reclaimed upon exit from + the procedure that called alloca. Originally, this space + was supposed to be taken from the current stack frame of the + caller, but that method cannot be made to work for some + implementations of C, for example under Gould's UTX/32. */ + +void * +alloca (size_t size) +{ + auto char probe; /* Probes stack depth: */ + register char *depth = ADDRESS_FUNCTION (probe); + +# if STACK_DIRECTION == 0 + if (STACK_DIR == 0) /* Unknown growth direction. */ + find_stack_direction (); +# endif + + /* Reclaim garbage, defined as all alloca'd storage that + was allocated from deeper in the stack than currently. */ + + { + register header *hp; /* Traverses linked list. */ + +# ifdef emacs + BLOCK_INPUT; +# endif + + for (hp = last_alloca_header; hp != NULL;) + if ((STACK_DIR > 0 && hp->h.deep > depth) + || (STACK_DIR < 0 && hp->h.deep < depth)) + { + register header *np = hp->h.next; + + free (hp); /* Collect garbage. */ + + hp = np; /* -> next header. */ + } + else + break; /* Rest are not deeper. */ + + last_alloca_header = hp; /* -> last valid storage. */ + +# ifdef emacs + UNBLOCK_INPUT; +# endif + } + + if (size == 0) + return NULL; /* No allocation required. */ + + /* Allocate combined header + user data storage. */ + + { + /* Address of header. */ + register header *new; + + size_t combined_size = sizeof (header) + size; + if (combined_size < sizeof (header)) + memory_full (); + + new = malloc (combined_size); + + if (! new) + memory_full (); + + new->h.next = last_alloca_header; + new->h.deep = depth; + + last_alloca_header = new; + + /* User storage begins just after header. */ + + return (void *) (new + 1); + } +} + +# if defined (CRAY) && defined (CRAY_STACKSEG_END) + +# ifdef DEBUG_I00AFUNC +# include <stdio.h> +# endif + +# ifndef CRAY_STACK +# define CRAY_STACK +# ifndef CRAY2 +/* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */ +struct stack_control_header + { + long shgrow:32; /* Number of times stack has grown. */ + long shaseg:32; /* Size of increments to stack. */ + long shhwm:32; /* High water mark of stack. */ + long shsize:32; /* Current size of stack (all segments). */ + }; + +/* The stack segment linkage control information occurs at + the high-address end of a stack segment. (The stack + grows from low addresses to high addresses.) The initial + part of the stack segment linkage control information is + 0200 (octal) words. This provides for register storage + for the routine which overflows the stack. */ + +struct stack_segment_linkage + { + long ss[0200]; /* 0200 overflow words. */ + long sssize:32; /* Number of words in this segment. */ + long ssbase:32; /* Offset to stack base. */ + long:32; + long sspseg:32; /* Offset to linkage control of previous + segment of stack. */ + long:32; + long sstcpt:32; /* Pointer to task common address block. */ + long sscsnm; /* Private control structure number for + microtasking. */ + long ssusr1; /* Reserved for user. */ + long ssusr2; /* Reserved for user. */ + long sstpid; /* Process ID for pid based multi-tasking. */ + long ssgvup; /* Pointer to multitasking thread giveup. */ + long sscray[7]; /* Reserved for Cray Research. */ + long ssa0; + long ssa1; + long ssa2; + long ssa3; + long ssa4; + long ssa5; + long ssa6; + long ssa7; + long sss0; + long sss1; + long sss2; + long sss3; + long sss4; + long sss5; + long sss6; + long sss7; + }; + +# else /* CRAY2 */ +/* The following structure defines the vector of words + returned by the STKSTAT library routine. */ +struct stk_stat + { + long now; /* Current total stack size. */ + long maxc; /* Amount of contiguous space which would + be required to satisfy the maximum + stack demand to date. */ + long high_water; /* Stack high-water mark. */ + long overflows; /* Number of stack overflow ($STKOFEN) calls. */ + long hits; /* Number of internal buffer hits. */ + long extends; /* Number of block extensions. */ + long stko_mallocs; /* Block allocations by $STKOFEN. */ + long underflows; /* Number of stack underflow calls ($STKRETN). */ + long stko_free; /* Number of deallocations by $STKRETN. */ + long stkm_free; /* Number of deallocations by $STKMRET. */ + long segments; /* Current number of stack segments. */ + long maxs; /* Maximum number of stack segments so far. */ + long pad_size; /* Stack pad size. */ + long current_address; /* Current stack segment address. */ + long current_size; /* Current stack segment size. This + number is actually corrupted by STKSTAT to + include the fifteen word trailer area. */ + long initial_address; /* Address of initial segment. */ + long initial_size; /* Size of initial segment. */ + }; + +/* The following structure describes the data structure which trails + any stack segment. I think that the description in 'asdef' is + out of date. I only describe the parts that I am sure about. */ + +struct stk_trailer + { + long this_address; /* Address of this block. */ + long this_size; /* Size of this block (does not include + this trailer). */ + long unknown2; + long unknown3; + long link; /* Address of trailer block of previous + segment. */ + long unknown5; + long unknown6; + long unknown7; + long unknown8; + long unknown9; + long unknown10; + long unknown11; + long unknown12; + long unknown13; + long unknown14; + }; + +# endif /* CRAY2 */ +# endif /* not CRAY_STACK */ + +# ifdef CRAY2 +/* Determine a "stack measure" for an arbitrary ADDRESS. + I doubt that "lint" will like this much. */ + +static long +i00afunc (long *address) +{ + struct stk_stat status; + struct stk_trailer *trailer; + long *block, size; + long result = 0; + + /* We want to iterate through all of the segments. The first + step is to get the stack status structure. We could do this + more quickly and more directly, perhaps, by referencing the + $LM00 common block, but I know that this works. */ + + STKSTAT (&status); + + /* Set up the iteration. */ + + trailer = (struct stk_trailer *) (status.current_address + + status.current_size + - 15); + + /* There must be at least one stack segment. Therefore it is + a fatal error if "trailer" is null. */ + + if (trailer == 0) + abort (); + + /* Discard segments that do not contain our argument address. */ + + while (trailer != 0) + { + block = (long *) trailer->this_address; + size = trailer->this_size; + if (block == 0 || size == 0) + abort (); + trailer = (struct stk_trailer *) trailer->link; + if ((block <= address) && (address < (block + size))) + break; + } + + /* Set the result to the offset in this segment and add the sizes + of all predecessor segments. */ + + result = address - block; + + if (trailer == 0) + { + return result; + } + + do + { + if (trailer->this_size <= 0) + abort (); + result += trailer->this_size; + trailer = (struct stk_trailer *) trailer->link; + } + while (trailer != 0); + + /* We are done. Note that if you present a bogus address (one + not in any segment), you will get a different number back, formed + from subtracting the address of the first block. This is probably + not what you want. */ + + return (result); +} + +# else /* not CRAY2 */ +/* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP. + Determine the number of the cell within the stack, + given the address of the cell. The purpose of this + routine is to linearize, in some sense, stack addresses + for alloca. */ + +static long +i00afunc (long address) +{ + long stkl = 0; + + long size, pseg, this_segment, stack; + long result = 0; + + struct stack_segment_linkage *ssptr; + + /* Register B67 contains the address of the end of the + current stack segment. If you (as a subprogram) store + your registers on the stack and find that you are past + the contents of B67, you have overflowed the segment. + + B67 also points to the stack segment linkage control + area, which is what we are really interested in. */ + + stkl = CRAY_STACKSEG_END (); + ssptr = (struct stack_segment_linkage *) stkl; + + /* If one subtracts 'size' from the end of the segment, + one has the address of the first word of the segment. + + If this is not the first segment, 'pseg' will be + nonzero. */ + + pseg = ssptr->sspseg; + size = ssptr->sssize; + + this_segment = stkl - size; + + /* It is possible that calling this routine itself caused + a stack overflow. Discard stack segments which do not + contain the target address. */ + + while (!(this_segment <= address && address <= stkl)) + { +# ifdef DEBUG_I00AFUNC + fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl); +# endif + if (pseg == 0) + break; + stkl = stkl - pseg; + ssptr = (struct stack_segment_linkage *) stkl; + size = ssptr->sssize; + pseg = ssptr->sspseg; + this_segment = stkl - size; + } + + result = address - this_segment; + + /* If you subtract pseg from the current end of the stack, + you get the address of the previous stack segment's end. + This seems a little convoluted to me, but I'll bet you save + a cycle somewhere. */ + + while (pseg != 0) + { +# ifdef DEBUG_I00AFUNC + fprintf (stderr, "%011o %011o\n", pseg, size); +# endif + stkl = stkl - pseg; + ssptr = (struct stack_segment_linkage *) stkl; + size = ssptr->sssize; + pseg = ssptr->sspseg; + result += size; + } + return (result); +} + +# endif /* not CRAY2 */ +# endif /* CRAY */ + +# endif /* no alloca */ +#endif /* not GCC version 3 */ diff --git a/gl/bind.c b/gl/bind.c new file mode 100644 index 0000000000..108a2393ce --- /dev/null +++ b/gl/bind.c @@ -0,0 +1,40 @@ +/* bind.c --- wrappers for Windows bind function + + Copyright (C) 2008 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Paolo Bonzini */ + +#include <config.h> + +#define WIN32_LEAN_AND_MEAN +/* Get winsock2.h. */ +#include <sys/socket.h> + +/* Get set_winsock_errno, FD_TO_SOCKET etc. */ +#include "w32sock.h" + +#undef bind + +int +rpl_bind (int fd, struct sockaddr *sockaddr, int len) +{ + SOCKET sock = FD_TO_SOCKET (fd); + int r = bind (sock, sockaddr, len); + if (r < 0) + set_winsock_errno (); + + return r; +} diff --git a/gl/close.c b/gl/close.c new file mode 100644 index 0000000000..74a7adfbbc --- /dev/null +++ b/gl/close.c @@ -0,0 +1,82 @@ +/* close replacement. + Copyright (C) 2008 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +/* Specification. */ +#include <unistd.h> + +#if GNULIB_SYS_SOCKET +# define WIN32_LEAN_AND_MEAN +# include <sys/socket.h> +#endif + +#if HAVE__GL_CLOSE_FD_MAYBE_SOCKET + +/* Get set_winsock_errno, FD_TO_SOCKET etc. */ +#include "w32sock.h" + +static int +_gl_close_fd_maybe_socket (int fd) +{ + SOCKET sock = FD_TO_SOCKET (fd); + WSANETWORKEVENTS ev; + + ev.lNetworkEvents = 0xDEADBEEF; + WSAEnumNetworkEvents (sock, NULL, &ev); + if (ev.lNetworkEvents != 0xDEADBEEF) + { + /* FIXME: other applications, like squid, use an undocumented + _free_osfhnd free function. But this is not enough: The 'osfile' + flags for fd also needs to be cleared, but it is hard to access it. + Instead, here we just close twice the file descriptor. */ + if (closesocket (sock)) + { + set_winsock_errno (); + return -1; + } + else + { + /* This call frees the file descriptor and does a + CloseHandle ((HANDLE) _get_osfhandle (fd)), which fails. */ + _close (fd); + return 0; + } + } + else + return _close (fd); +} +#endif + +/* Override close() to call into other gnulib modules. */ + +int +rpl_close (int fd) +#undef close +{ +#if HAVE__GL_CLOSE_FD_MAYBE_SOCKET + int retval = _gl_close_fd_maybe_socket (fd); +#else + int retval = close (fd); +#endif + +#ifdef FCHDIR_REPLACEMENT + if (retval >= 0) + _gl_unregister_fd (fd); +#endif + + return retval; +} diff --git a/gl/fclose.c b/gl/fclose.c new file mode 100644 index 0000000000..a0074b77d7 --- /dev/null +++ b/gl/fclose.c @@ -0,0 +1,47 @@ +/* fclose replacement. + Copyright (C) 2008 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +/* Specification. */ +#include <stdio.h> + +#include <errno.h> +#include <unistd.h> + +/* Override fclose() to call the overridden close(). */ + +int +rpl_fclose (FILE *fp) +#undef fclose +{ + int saved_errno = 0; + + if (fflush (fp)) + saved_errno = errno; + + if (close (fileno (fp)) < 0 && saved_errno == 0) + saved_errno = errno; + + fclose (fp); /* will fail with errno = EBADF */ + + if (saved_errno != 0) + { + errno = saved_errno; + return EOF; + } + return 0; +} diff --git a/gl/listen.c b/gl/listen.c new file mode 100644 index 0000000000..49e0a08425 --- /dev/null +++ b/gl/listen.c @@ -0,0 +1,40 @@ +/* listen.c --- wrappers for Windows listen function + + Copyright (C) 2008 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Paolo Bonzini */ + +#include <config.h> + +#define WIN32_LEAN_AND_MEAN +/* Get winsock2.h. */ +#include <sys/socket.h> + +/* Get set_winsock_errno, FD_TO_SOCKET etc. */ +#include "w32sock.h" + +#undef listen + +int +rpl_listen (int fd, int backlog) +{ + SOCKET sock = FD_TO_SOCKET (fd); + int r = listen (sock, backlog); + if (r < 0) + set_winsock_errno (); + + return r; +} diff --git a/gl/m4/close.m4 b/gl/m4/close.m4 new file mode 100644 index 0000000000..fcc9fb1952 --- /dev/null +++ b/gl/m4/close.m4 @@ -0,0 +1,25 @@ +# close.m4 serial 2 +dnl Copyright (C) 2008 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_FUNC_CLOSE], +[ + m4_ifdef([gl_PREREQ_SYS_H_WINSOCK2], [ + gl_PREREQ_SYS_H_WINSOCK2 + if test $UNISTD_H_HAVE_WINSOCK2_H = 1; then + gl_REPLACE_CLOSE + fi + ]) +]) + +AC_DEFUN([gl_REPLACE_CLOSE], +[ + AC_REQUIRE([gl_UNISTD_H_DEFAULTS]) + if test $REPLACE_CLOSE != 1; then + AC_LIBOBJ([close]) + fi + REPLACE_CLOSE=1 + gl_REPLACE_FCLOSE +]) diff --git a/gl/m4/fclose.m4 b/gl/m4/fclose.m4 new file mode 100644 index 0000000000..d10c104fa9 --- /dev/null +++ b/gl/m4/fclose.m4 @@ -0,0 +1,18 @@ +# fclose.m4 serial 1 +dnl Copyright (C) 2008 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_FUNC_FCLOSE], +[ +]) + +AC_DEFUN([gl_REPLACE_FCLOSE], +[ + AC_REQUIRE([gl_STDIO_H_DEFAULTS]) + if test $REPLACE_FCLOSE != 1; then + AC_LIBOBJ([fclose]) + fi + REPLACE_FCLOSE=1 +]) diff --git a/gl/m4/perror.m4 b/gl/m4/perror.m4 new file mode 100644 index 0000000000..fb37fd0d99 --- /dev/null +++ b/gl/m4/perror.m4 @@ -0,0 +1,17 @@ +# perror.m4 serial 1 +dnl Copyright (C) 2008 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_FUNC_PERROR], +[ + AC_REQUIRE([gl_STDIO_H_DEFAULTS]) + AC_REQUIRE([gl_HEADER_ERRNO_H]) + if test -n "$ERRNO_H"; then + dnl The system's perror() cannot know about the new errno values we add + dnl to <errno.h>. Replace it. + REPLACE_PERROR=1 + AC_LIBOBJ([perror]) + fi +]) diff --git a/gl/m4/sockets.m4 b/gl/m4/sockets.m4 new file mode 100644 index 0000000000..c7bd6646a6 --- /dev/null +++ b/gl/m4/sockets.m4 @@ -0,0 +1,65 @@ +# sockets.m4 serial 2 +dnl Copyright (C) 2008 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_SOCKETS], +[ + gl_PREREQ_SYS_H_WINSOCK2 dnl for HAVE_WINSOCK2_H + LIBSOCKET= + if test $HAVE_WINSOCK2_H = 1; then + dnl Native Windows API (not Cygwin). + AC_CACHE_CHECK([if we need to call WSAStartup in winsock2.h and -lws2_32], + [gl_cv_func_wsastartup], [ + gl_save_LIBS="$LIBS" + LIBS="$LIBS -lws2_32" + AC_TRY_LINK([ +#ifdef HAVE_WINSOCK2_H +# include <winsock2.h> +#endif], [ + WORD wVersionRequested = MAKEWORD(1, 1); + WSADATA wsaData; + int err = WSAStartup(wVersionRequested, &wsaData); + WSACleanup ();], + gl_cv_func_wsastartup=yes, gl_cv_func_wsastartup=no) + LIBS="$gl_save_LIBS" + ]) + if test "$gl_cv_func_wsastartup" = "yes"; then + AC_DEFINE([WINDOWS_SOCKETS], 1, [Define if WSAStartup is needed.]) + LIBSOCKET='-lws2_32' + fi + else + dnl Unix API. + dnl Solaris has most socket functions in libsocket. + AC_CACHE_CHECK([whether setsockopt requires -lsocket], [gl_cv_lib_socket], [ + gl_cv_lib_socket=no + AC_TRY_LINK([extern +#ifdef __cplusplus +"C" +#endif +char setsockopt();], [setsockopt();], + [], + [gl_save_LIBS="$LIBS" + LIBS="$LIBS -lsocket" + AC_TRY_LINK([extern +#ifdef __cplusplus +"C" +#endif +char setsockopt();], [setsockopt();], + [gl_cv_lib_socket=yes]) + LIBS="$gl_save_LIBS" + ]) + ]) + if test $gl_cv_lib_socket = yes; then + LIBSOCKET='-lsocket' + fi + fi + AC_SUBST([LIBSOCKET]) + gl_PREREQ_SOCKETS +]) + +# Prerequisites of lib/sockets.c. +AC_DEFUN([gl_PREREQ_SOCKETS], [ + : +]) diff --git a/gl/m4/sys_ioctl_h.m4 b/gl/m4/sys_ioctl_h.m4 new file mode 100644 index 0000000000..5488039b6c --- /dev/null +++ b/gl/m4/sys_ioctl_h.m4 @@ -0,0 +1,60 @@ +# sys_ioctl_h.m4 serial 1 +dnl Copyright (C) 2008 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl Written by Bruno Haible. + +AC_DEFUN([gl_SYS_IOCTL_H], +[ + dnl Use AC_REQUIRE here, so that the default behavior below is expanded + dnl once only, before all statements that occur in other macros. + AC_REQUIRE([gl_SYS_IOCTL_H_DEFAULTS]) + + AC_CHECK_HEADERS_ONCE([sys/ioctl.h]) + if test $ac_cv_header_sys_ioctl_h = yes; then + HAVE_SYS_IOCTL_H=1 + dnl Test whether <sys/ioctl.h> declares ioctl(), or whether some other + dnl header file, such as <unistd.h> or <stropts.h>, is needed for that. + AC_CACHE_CHECK([whether <sys/ioctl.h> declares ioctl], + [gl_cv_decl_ioctl_in_sys_ioctl_h], + [AC_CHECK_DECL([ioctl], + [gl_cv_decl_ioctl_in_sys_ioctl_h=yes], + [gl_cv_decl_ioctl_in_sys_ioctl_h=no], + [#include <sys/ioctl.h>]) + ]) + if test $gl_cv_decl_ioctl_in_sys_ioctl_h != yes; then + SYS_IOCTL_H='sys/ioctl.h' + fi + else + HAVE_SYS_IOCTL_H=0 + SYS_IOCTL_H='sys/ioctl.h' + fi + AC_SUBST([HAVE_SYS_IOCTL_H]) + dnl Execute this unconditionally, because SYS_IOCTL_H may be set by other + dnl modules, after this code is executed. + gl_CHECK_NEXT_HEADERS([sys/ioctl.h]) +]) + +dnl Unconditionally enables the replacement of <sys/ioctl.h>. +AC_DEFUN([gl_REPLACE_SYS_IOCTL_H], +[ + AC_REQUIRE([gl_SYS_IOCTL_H_DEFAULTS]) + SYS_IOCTL_H='sys/ioctl.h' +]) + +AC_DEFUN([gl_SYS_IOCTL_MODULE_INDICATOR], +[ + dnl Use AC_REQUIRE here, so that the default settings are expanded once only. + AC_REQUIRE([gl_SYS_IOCTL_H_DEFAULTS]) + GNULIB_[]m4_translit([$1],[abcdefghijklmnopqrstuvwxyz./-],[ABCDEFGHIJKLMNOPQRSTUVWXYZ___])=1 +]) + +AC_DEFUN([gl_SYS_IOCTL_H_DEFAULTS], +[ + GNULIB_IOCTL=0; AC_SUBST([GNULIB_IOCTL]) + dnl Assume proper GNU behavior unless another module says otherwise. + SYS_IOCTL_H_HAVE_WINSOCK2_H=0; AC_SUBST([SYS_IOCTL_H_HAVE_WINSOCK2_H]) + SYS_IOCTL_H=''; AC_SUBST([SYS_IOCTL_H]) +]) diff --git a/gl/m4/sys_select_h.m4 b/gl/m4/sys_select_h.m4 new file mode 100644 index 0000000000..c537e7918a --- /dev/null +++ b/gl/m4/sys_select_h.m4 @@ -0,0 +1,45 @@ +# sys_select_h.m4 serial 6 +dnl Copyright (C) 2006-2008 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_HEADER_SYS_SELECT], +[ + AC_REQUIRE([gl_HEADER_SYS_SOCKET]) + AC_REQUIRE([gl_SYS_SELECT_H_DEFAULTS]) + AC_CACHE_CHECK([whether <sys/select.h> is self-contained], + [gl_cv_header_sys_select_h_selfcontained], + [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/select.h>]], + [[struct timeval b;]])], + [gl_cv_header_sys_select_h_selfcontained=yes], + [gl_cv_header_sys_select_h_selfcontained=no]) + ]) + if test $gl_cv_header_sys_select_h_selfcontained = yes; then + SYS_SELECT_H='' + else + SYS_SELECT_H='sys/select.h' + gl_CHECK_NEXT_HEADERS([sys/select.h]) + if test $ac_cv_header_sys_select_h = yes; then + HAVE_SYS_SELECT_H=1 + else + HAVE_SYS_SELECT_H=0 + fi + AC_SUBST([HAVE_SYS_SELECT_H]) + gl_PREREQ_SYS_H_WINSOCK2 + fi + AC_SUBST([SYS_SELECT_H]) +]) + +AC_DEFUN([gl_SYS_SELECT_MODULE_INDICATOR], +[ + dnl Use AC_REQUIRE here, so that the default settings are expanded once only. + AC_REQUIRE([gl_SYS_SELECT_H_DEFAULTS]) + GNULIB_[]m4_translit([$1],[abcdefghijklmnopqrstuvwxyz./-],[ABCDEFGHIJKLMNOPQRSTUVWXYZ___])=1 +]) + +AC_DEFUN([gl_SYS_SELECT_H_DEFAULTS], +[ + GNULIB_SELECT=0; AC_SUBST([GNULIB_SELECT]) +]) diff --git a/gl/m4/sys_time_h.m4 b/gl/m4/sys_time_h.m4 new file mode 100644 index 0000000000..13ac5763b3 --- /dev/null +++ b/gl/m4/sys_time_h.m4 @@ -0,0 +1,57 @@ +# Configure a replacement for <sys/time.h>. + +# Copyright (C) 2007 Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# Written by Paul Eggert and Martin Lambers. + +AC_DEFUN([gl_HEADER_SYS_TIME_H], +[ + dnl Use AC_REQUIRE here, so that the REPLACE_GETTIMEOFDAY=0 statement + dnl below is expanded once only, before all REPLACE_GETTIMEOFDAY=1 + dnl statements that occur in other macros. + AC_REQUIRE([gl_HEADER_SYS_TIME_H_BODY]) +]) + +AC_DEFUN([gl_HEADER_SYS_TIME_H_BODY], +[ + AC_REQUIRE([AC_C_RESTRICT]) + gl_CHECK_NEXT_HEADERS([sys/time.h]) + + if test $ac_cv_header_sys_time_h = yes; then + HAVE_SYS_TIME_H=1 + else + HAVE_SYS_TIME_H=0 + fi + AC_SUBST([HAVE_SYS_TIME_H]) + + AC_CACHE_CHECK([for struct timeval], [gl_cv_sys_struct_timeval], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#if HAVE_SYS_TIME_H + #include <sys/time.h> + #endif + #include <time.h> + ]], + [[static struct timeval x; x.tv_sec = x.tv_usec;]])], + [gl_cv_sys_struct_timeval=yes], + [gl_cv_sys_struct_timeval=no])]) + if test $gl_cv_sys_struct_timeval = yes; then + HAVE_STRUCT_TIMEVAL=1 + else + HAVE_STRUCT_TIMEVAL=0 + fi + AC_SUBST([HAVE_STRUCT_TIMEVAL]) + + dnl Assume POSIX behavior unless another module says otherwise. + REPLACE_GETTIMEOFDAY=0 + AC_SUBST([REPLACE_GETTIMEOFDAY]) + if test $HAVE_SYS_TIME_H = 0 || test $HAVE_STRUCT_TIMEVAL = 0; then + SYS_TIME_H=sys/time.h + else + SYS_TIME_H= + fi + AC_SUBST([SYS_TIME_H]) +]) diff --git a/gl/setsockopt.c b/gl/setsockopt.c new file mode 100644 index 0000000000..09f048e48a --- /dev/null +++ b/gl/setsockopt.c @@ -0,0 +1,40 @@ +/* setsockopt.c --- wrappers for Windows setsockopt function + + Copyright (C) 2008 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Paolo Bonzini */ + +#include <config.h> + +#define WIN32_LEAN_AND_MEAN +/* Get winsock2.h. */ +#include <sys/socket.h> + +/* Get set_winsock_errno, FD_TO_SOCKET etc. */ +#include "w32sock.h" + +#undef setsockopt + +int +rpl_setsockopt (int fd, int level, int optname, const void *optval, int optlen) +{ + SOCKET sock = FD_TO_SOCKET (fd); + int r = setsockopt (sock, level, optname, optval, optlen); + if (r < 0) + set_winsock_errno (); + + return r; +} diff --git a/gl/socket.c b/gl/socket.c new file mode 100644 index 0000000000..1fa6928747 --- /dev/null +++ b/gl/socket.c @@ -0,0 +1,43 @@ +/* socket.c --- wrappers for Windows socket function + + Copyright (C) 2008 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Paolo Bonzini */ + +#include <config.h> + +#define WIN32_LEAN_AND_MEAN +/* Get winsock2.h. */ +#include <sys/socket.h> + +/* Get set_winsock_errno, FD_TO_SOCKET etc. */ +#include "w32sock.h" + +int +rpl_socket (int domain, int type, int protocol) +{ + /* We have to use WSASocket() to create non-overlapped IO sockets. + Overlapped IO sockets cannot be used with read/write. */ + SOCKET fh = WSASocket (domain, type, protocol, NULL, 0, 0); + + if (fh == INVALID_SOCKET) + { + set_winsock_errno (); + return -1; + } + else + return SOCKET_TO_FD (fh); +} diff --git a/gl/sys_select.in.h b/gl/sys_select.in.h new file mode 100644 index 0000000000..86f35d31d8 --- /dev/null +++ b/gl/sys_select.in.h @@ -0,0 +1,78 @@ +/* Substitute for <sys/select.h>. + Copyright (C) 2007-2008 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _GL_SYS_SELECT_H + +#if @HAVE_SYS_SELECT_H@ + +# if __GNUC__ >= 3 +@PRAGMA_SYSTEM_HEADER@ +# endif + +/* On many platforms, <sys/select.h> assumes prior inclusion of + <sys/types.h>. */ +# include <sys/types.h> + +/* On OSF/1 4.0, <sys/select.h> provides only a forward declaration + of 'struct timeval', and no definition of this type.. */ +# include <sys/time.h> + +/* The include_next requires a split double-inclusion guard. */ +# @INCLUDE_NEXT@ @NEXT_SYS_SELECT_H@ + +#endif + +#ifndef _GL_SYS_SELECT_H +#define _GL_SYS_SELECT_H + +#if !@HAVE_SYS_SELECT_H@ + +/* A platform that lacks <sys/select.h>. */ + +# include <sys/socket.h> + +/* The definition of GL_LINK_WARNING is copied here. */ + +# ifdef __cplusplus +extern "C" { +# endif + +# if @GNULIB_SELECT@ +# if @HAVE_WINSOCK2_H@ +# undef select +# define select rpl_select +extern int rpl_select (int, fd_set *, fd_set *, fd_set *, struct timeval *); +# endif +# elif @HAVE_WINSOCK2_H@ +# undef select +# define select select_used_without_requesting_gnulib_module_select +# elif defined GNULIB_POSIXCHECK +# undef select +# define select(n,r,w,e,t) \ + (GL_LINK_WARNING ("select is not always POSIX compliant - " \ + "use gnulib module select for portability"), \ + select (n, r, w, e, t)) +# endif + +# ifdef __cplusplus +} +# endif + +#endif + +#endif /* _GL_SYS_SELECT_H */ +#endif /* _GL_SYS_SELECT_H */ diff --git a/gl/sys_time.in.h b/gl/sys_time.in.h new file mode 100644 index 0000000000..1a0f241ce3 --- /dev/null +++ b/gl/sys_time.in.h @@ -0,0 +1,56 @@ +/* Provide a more complete sys/time.h. + + Copyright (C) 2007-2008 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Paul Eggert. */ + +#if __GNUC__ >= 3 +@PRAGMA_SYSTEM_HEADER@ +#endif + +#if defined _GL_SYS_TIME_H + +/* Simply delegate to the system's header, without adding anything. */ +# if @HAVE_SYS_TIME_H@ +# @INCLUDE_NEXT@ @NEXT_SYS_TIME_H@ +# endif + +#else + +# define _GL_SYS_TIME_H + +# if @HAVE_SYS_TIME_H@ +# @INCLUDE_NEXT@ @NEXT_SYS_TIME_H@ +# else +# include <time.h> +# endif + +# if ! @HAVE_STRUCT_TIMEVAL@ +struct timeval +{ + time_t tv_sec; + long int tv_usec; +}; +# endif + +# if @REPLACE_GETTIMEOFDAY@ +# undef gettimeofday +# define gettimeofday rpl_gettimeofday +int gettimeofday (struct timeval *restrict, void *restrict); +# endif + +#endif /* _GL_SYS_TIME_H */ diff --git a/gl/tests/connect.c b/gl/tests/connect.c new file mode 100644 index 0000000000..c7abf204d2 --- /dev/null +++ b/gl/tests/connect.c @@ -0,0 +1,47 @@ +/* connect.c --- wrappers for Windows connect function + + Copyright (C) 2008 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Paolo Bonzini */ + +#include <config.h> + +#define WIN32_LEAN_AND_MEAN +/* Get winsock2.h. */ +#include <sys/socket.h> + +/* Get set_winsock_errno, FD_TO_SOCKET etc. */ +#include "w32sock.h" + +#undef connect + +int +rpl_connect (int fd, struct sockaddr *sockaddr, int len) +{ + SOCKET sock = FD_TO_SOCKET (fd); + int r = connect (sock, sockaddr, len); + if (r < 0) + { + /* EINPROGRESS is not returned by WinSock 2.0; for backwards + compatibility, connect(2) uses EWOULDBLOCK. */ + if (WSAGetLastError () == WSAEWOULDBLOCK) + WSASetLastError (WSAEINPROGRESS); + + set_winsock_errno (); + } + + return r; +} diff --git a/gl/tests/ioctl.c b/gl/tests/ioctl.c new file mode 100644 index 0000000000..a23d363bc5 --- /dev/null +++ b/gl/tests/ioctl.c @@ -0,0 +1,49 @@ +/* ioctl.c --- wrappers for Windows ioctl function + + Copyright (C) 2008 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Paolo Bonzini */ + +#include <config.h> + +#include <stdarg.h> + +#define WIN32_LEAN_AND_MEAN +/* Get winsock2.h. */ +#include <sys/socket.h> + +/* Get set_winsock_errno, FD_TO_SOCKET etc. */ +#include "w32sock.h" + +int +rpl_ioctl (int fd, int req, ...) +{ + void *buf; + va_list args; + SOCKET sock; + int r; + + va_start (args, req); + buf = va_arg (args, void *); + va_end (args); + + sock = FD_TO_SOCKET (fd); + r = ioctlsocket (sock, req, buf); + if (r < 0) + set_winsock_errno (); + + return r; +} diff --git a/gl/tests/perror.c b/gl/tests/perror.c new file mode 100644 index 0000000000..72d9f28514 --- /dev/null +++ b/gl/tests/perror.c @@ -0,0 +1,35 @@ +/* Print a message describing error code. + Copyright (C) 2008 Free Software Foundation, Inc. + Written by Bruno Haible and Simon Josefsson. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +/* Specification. */ +#include <stdio.h> + +#include <errno.h> +#include <string.h> + +void +perror (const char *string) +{ + const char *errno_description = strerror (errno); + + if (string != NULL && *string != '\0') + fprintf (stderr, "%s: %s\n", string, errno_description); + else + fprintf (stderr, "%s\n", errno_description); +} diff --git a/gl/tests/sockets.c b/gl/tests/sockets.c new file mode 100644 index 0000000000..658119ea43 --- /dev/null +++ b/gl/tests/sockets.c @@ -0,0 +1,57 @@ +/* sockets.c --- wrappers for Windows socket functions + + Copyright (C) 2008 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Simon Josefsson */ + +#include <config.h> + +/* This includes winsock2.h on MinGW. */ +#include <sys/socket.h> + +#include "sockets.h" + +int +gl_sockets_startup (int version) +{ +#if WINDOWS_SOCKETS + WSADATA data; + int err; + + err = WSAStartup (version, &data); + if (err != 0) + return 1; + + if (data.wVersion < version) + return 2; +#endif + + return 0; +} + +int +gl_sockets_cleanup (void) +{ +#if WINDOWS_SOCKETS + int err; + + err = WSACleanup (); + if (err != 0) + return 1; +#endif + + return 0; +} diff --git a/gl/tests/sockets.h b/gl/tests/sockets.h new file mode 100644 index 0000000000..3ab16a0d50 --- /dev/null +++ b/gl/tests/sockets.h @@ -0,0 +1,32 @@ +/* sockets.h - wrappers for Windows socket functions + + Copyright (C) 2008 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Simon Josefsson */ + +#ifndef SOCKETS_H +# define SOCKETS_H 1 + +#define SOCKETS_1_0 0x100 +#define SOCKETS_1_1 0x101 +#define SOCKETS_2_0 0x200 +#define SOCKETS_2_1 0x201 +#define SOCKETS_2_2 0x202 + +int gl_sockets_startup (int version); +int gl_sockets_cleanup (void); + +#endif diff --git a/gl/tests/sys_ioctl.in.h b/gl/tests/sys_ioctl.in.h new file mode 100644 index 0000000000..65c2c4a452 --- /dev/null +++ b/gl/tests/sys_ioctl.in.h @@ -0,0 +1,70 @@ +/* Substitute for and wrapper around <sys/ioctl.h>. + Copyright (C) 2008 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _GL_SYS_IOCTL_H + +#if __GNUC__ >= 3 +@PRAGMA_SYSTEM_HEADER@ +#endif + +/* The include_next requires a split double-inclusion guard. */ +#if @HAVE_SYS_IOCTL_H@ +# @INCLUDE_NEXT@ @NEXT_SYS_IOCTL_H@ +#endif + +#ifndef _GL_SYS_IOCTL_H +#define _GL_SYS_IOCTL_H + +/* AIX 5.1 and Solaris 10 declare ioctl() in <unistd.h> and in <stropts.h>, + but not in <sys/ioctl.h>. */ +#include <unistd.h> + +/* The definition of GL_LINK_WARNING is copied here. */ + + +/* Declare overridden functions. */ + +#ifdef __cplusplus +extern "C" { +#endif + + +#if @GNULIB_IOCTL@ +# if @SYS_IOCTL_H_HAVE_WINSOCK2_H@ +# undef ioctl +# define ioctl rpl_ioctl +extern int ioctl (int fd, int request, ... /* {void *,char *} arg */); +# endif +#elif @SYS_IOCTL_H_HAVE_WINSOCK2_H@ +# undef ioctl +# define ioctl ioctl_used_without_requesting_gnulib_module_ioctl +#elif defined GNULIB_POSIXCHECK +# undef ioctl +# define ioctl(f,c,a) \ + (GL_LINK_WARNING ("ioctl does not portably work on sockets - " \ + "use gnulib module ioctl for portability"), \ + ioctl (f, c, a)) +#endif + + +#ifdef __cplusplus +} +#endif + + +#endif /* _GL_SYS_IOCTL_H */ +#endif /* _GL_SYS_IOCTL_H */ diff --git a/gl/tests/test-perror.c b/gl/tests/test-perror.c new file mode 100644 index 0000000000..2faa8aeaf0 --- /dev/null +++ b/gl/tests/test-perror.c @@ -0,0 +1,34 @@ +/* Test of perror() function. + Copyright (C) 2008 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include <config.h> + +#include <stdio.h> + +#include <errno.h> + +int +main (int argc, char **argv) +{ + const char *prefix = (argc > 1 ? argv[1] : NULL); + + errno = EACCES; perror (prefix); + errno = ETIMEDOUT; perror (prefix); + errno = EOVERFLOW; perror (prefix); + + return 0; +} diff --git a/gl/tests/test-perror.sh b/gl/tests/test-perror.sh new file mode 100755 index 0000000000..f2c8fdc336 --- /dev/null +++ b/gl/tests/test-perror.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +tmpfiles="" +trap 'rm -fr $tmpfiles' 1 2 3 15 + +# Test NULL prefix. Result should not contain a number. +tmpfiles="$tmpfiles t-perror.tmp" +./test-perror${EXEEXT} 2>&1 >/dev/null | LC_ALL=C tr -d '\r' > t-perror.tmp +if grep '[0-9]' t-perror.tmp > /dev/null; then + rm -fr $tmpfiles; exit 1 +fi + +# Test empty prefix. Result should be the same. +tmpfiles="$tmpfiles t-perror1.tmp" +./test-perror${EXEEXT} '' 2>&1 >/dev/null | LC_ALL=C tr -d '\r' > t-perror1.tmp +diff t-perror.tmp t-perror1.tmp +test $? = 0 || { rm -fr $tmpfiles; exit 1; } + +# Test non-empty prefix. +tmpfiles="$tmpfiles t-perror2.tmp t-perror3.tmp" +./test-perror${EXEEXT} 'foo' 2>&1 >/dev/null | LC_ALL=C tr -d '\r' > t-perror3.tmp +sed -e 's/^/foo: /' < t-perror.tmp > t-perror2.tmp +diff t-perror2.tmp t-perror3.tmp +test $? = 0 || { rm -fr $tmpfiles; exit 1; } + +rm -fr $tmpfiles +exit 0 diff --git a/gl/tests/test-select.c b/gl/tests/test-select.c new file mode 100644 index 0000000000..4c9cdc02dc --- /dev/null +++ b/gl/tests/test-select.c @@ -0,0 +1,376 @@ +/* Test of select() substitute. + Copyright (C) 2008 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Paolo Bonzini, 2008. */ + +#include <config.h> + +#include <sys/select.h> + +#include <stdio.h> +#include <string.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdbool.h> +#include <sys/ioctl.h> +#include <errno.h> +#include "sockets.h" + +enum { SEL_IN = 1, SEL_OUT = 2, SEL_EXC = 4 }; + +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ +# define WIN32_NATIVE +#endif + +#ifdef WIN32_NATIVE +#include <io.h> +#define pipe(x) _pipe(x, 256, O_BINARY) +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif + +#ifndef SO_REUSEPORT +#define SO_REUSEPORT SO_REUSEADDR +#endif + +#define TEST_PORT 12345 + + +/* Minimal testing infrastructure. */ + +static int failures; + +static void +failed (const char *reason) +{ + if (++failures > 1) + printf (" "); + printf ("failed (%s)\n", reason); +} + +static int +test (void (*fn) (void), const char *msg) +{ + failures = 0; + printf ("%s... ", msg); + fflush (stdout); + fn (); + + if (!failures) + printf ("passed\n"); + + return failures; +} + + +/* Funny socket code. */ + +static int +open_server_socket () +{ + int s, x; + struct sockaddr_in ia; + + s = socket (AF_INET, SOCK_STREAM, 0); + + memset (&ia, 0, sizeof (ia)); + ia.sin_family = AF_INET; + inet_pton (AF_INET, "127.0.0.1", &ia.sin_addr); + ia.sin_port = htons (TEST_PORT); + if (bind (s, (struct sockaddr *) &ia, sizeof (ia)) < 0) + { + perror ("bind"); + exit (77); + } + + x = 1; + setsockopt (s, SOL_SOCKET, SO_REUSEPORT, &x, sizeof (x)); + + if (listen (s, 1) < 0) + { + perror ("listen"); + exit (77); + } + + return s; +} + +static int +connect_to_socket (int blocking) +{ + int s; + struct sockaddr_in ia; + + s = socket (AF_INET, SOCK_STREAM, 0); + + memset (&ia, 0, sizeof (ia)); + ia.sin_family = AF_INET; + inet_pton (AF_INET, "127.0.0.1", &ia.sin_addr); + ia.sin_port = htons (TEST_PORT); + + if (!blocking) + { +#ifdef WIN32_NATIVE + unsigned long iMode = 1; + ioctl (s, FIONBIO, (char *) &iMode); + +#elif defined F_GETFL + int oldflags = fcntl (s, F_GETFL, NULL); + + if (!(oldflags & O_NONBLOCK)) + fcntl (s, F_SETFL, oldflags | O_NONBLOCK); +#endif + } + + if (connect (s, (struct sockaddr *) &ia, sizeof (ia)) < 0 + && (blocking || errno != EINPROGRESS)) + { + perror ("connect"); + exit (77); + } + + return s; +} + + +/* A slightly more convenient interface to select(2). */ + +static int +do_select (int fd, int ev, struct timeval *tv) +{ + fd_set rfds, wfds, xfds; + int r, rev; + + FD_ZERO (&rfds); + FD_ZERO (&wfds); + FD_ZERO (&xfds); + if (ev & SEL_IN) + FD_SET (fd, &rfds); + if (ev & SEL_OUT) + FD_SET (fd, &wfds); + if (ev & SEL_EXC) + FD_SET (fd, &xfds); + r = select (fd + 1, &rfds, &wfds, &xfds, tv); + if (r < 0) + return r; + + rev = 0; + if (FD_ISSET (fd, &rfds)) + rev |= SEL_IN; + if (FD_ISSET (fd, &wfds)) + rev |= SEL_OUT; + if (FD_ISSET (fd, &xfds)) + rev |= SEL_EXC; + if (rev && r == 0) + failed ("select returned 0"); + if (rev & ~ev) + failed ("select returned unrequested events"); + + return rev; +} + +static int +do_select_nowait (int fd, int ev) +{ + static struct timeval tv0; + return do_select (fd, ev, &tv0); +} + +static int +do_select_wait (int fd, int ev) +{ + return do_select (fd, ev, NULL); +} + + +/* Test poll(2) for TTYs. */ + +#ifdef INTERACTIVE +static void +test_tty (void) +{ + if (do_select_nowait (0, SEL_IN) != 0) + failed ("can read"); + if (do_select_nowait (0, SEL_OUT) == 0) + failed ("cannot write"); + + if (do_select_wait (0, SEL_IN) == 0) + failed ("return with infinite timeout"); + + getchar (); + if (do_select_nowait (0, SEL_IN) != 0) + failed ("can read after getc"); +} +#endif + + +/* Test poll(2) for unconnected nonblocking sockets. */ + +static void +test_connect_first (void) +{ + int s = open_server_socket (); + struct sockaddr_in ia; + socklen_t addrlen; + + int c1, c2; + + if (do_select_nowait (s, SEL_IN | SEL_EXC) != 0) + failed ("can read, socket not connected"); + + c1 = connect_to_socket (false); + + if (do_select_wait (s, SEL_IN | SEL_EXC) != SEL_IN) + failed ("expecting readability on passive socket"); + if (do_select_nowait (s, SEL_IN | SEL_EXC) != SEL_IN) + failed ("expecting readability on passive socket"); + + addrlen = sizeof (ia); + c2 = accept (s, (struct sockaddr *) &ia, &addrlen); + close (s); + close (c1); + close (c2); +} + + +/* Test poll(2) for unconnected blocking sockets. */ + +static void +test_accept_first (void) +{ +#ifndef WIN32_NATIVE + int s = open_server_socket (); + struct sockaddr_in ia; + socklen_t addrlen; + char buf[3]; + int c, pid; + + pid = fork (); + if (pid < 0) + return; + + if (pid == 0) + { + addrlen = sizeof (ia); + c = accept (s, (struct sockaddr *) &ia, &addrlen); + close (s); + write (c, "foo", 3); + read (c, buf, 3); + shutdown (c, SHUT_RD); + close (c); + exit (0); + } + else + { + close (s); + c = connect_to_socket (true); + if (do_select_nowait (c, SEL_OUT) != SEL_OUT) + failed ("cannot write after blocking connect"); + write (c, "foo", 3); + wait (&pid); + if (do_select_wait (c, SEL_IN) != SEL_IN) + failed ("cannot read data left in the socket by closed process"); + read (c, buf, 3); + write (c, "foo", 3); + close (c); + } +#endif +} + + +/* Common code for pipes and connected sockets. */ + +static void +test_pair (int rd, int wd) +{ + char buf[3]; + if (do_select_wait (wd, SEL_IN | SEL_OUT | SEL_EXC) != SEL_OUT) + failed ("expecting writability before writing"); + if (do_select_nowait (wd, SEL_IN | SEL_OUT | SEL_EXC) != SEL_OUT) + failed ("expecting writability before writing"); + + write (wd, "foo", 3); + if (do_select_wait (rd, SEL_IN) != SEL_IN) + failed ("expecting readability after writing"); + if (do_select_nowait (rd, SEL_IN) != SEL_IN) + failed ("expecting readability after writing"); + + read (rd, buf, 3); +} + + +/* Test poll(2) on connected sockets. */ + +static void +test_socket_pair (void) +{ + struct sockaddr_in ia; + + socklen_t addrlen = sizeof (ia); + int s = open_server_socket (); + int c1 = connect_to_socket (false); + int c2 = accept (s, (struct sockaddr *) &ia, &addrlen); + + close (s); + + test_pair (c1, c2); + close (c1); + write (c2, "foo", 3); + close (c2); +} + + +/* Test poll(2) on pipes. */ + +static void +test_pipe (void) +{ + int fd[2]; + + pipe (fd); + test_pair (fd[0], fd[1]); + close (fd[0]); + close (fd[1]); +} + + +/* Do them all. */ + +int +main () +{ + int result; + + gl_sockets_startup (SOCKETS_1_1); + +#ifdef INTERACTIVE + printf ("Please press Enter\n"); + test (test_tty, "TTY"); +#endif + + result = test (test_connect_first, "Unconnected socket test"); + result += test (test_socket_pair, "Connected sockets test"); + result += test (test_accept_first, "General socket test with fork"); + result += test (test_pipe, "Pipe test"); + + exit (result); +} diff --git a/gl/tests/test-sockets.c b/gl/tests/test-sockets.c new file mode 100644 index 0000000000..514409d95b --- /dev/null +++ b/gl/tests/test-sockets.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2008 Free Software Foundation + * Written by Simon Josefsson. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +#include <stdio.h> + +#include "sockets.h" + +int +main (int argc, char *argv[]) +{ + int err; + + err = gl_sockets_startup (SOCKETS_1_1); + if (err != 0) + { + printf ("wsastartup failed %d\n", err); + return 1; + } + + err = gl_sockets_cleanup (); + if (err != 0) + { + printf ("wsacleanup failed %d\n", err); + return 1; + } + + return 0; +} diff --git a/gl/tests/test-sys_select.c b/gl/tests/test-sys_select.c new file mode 100644 index 0000000000..6449dafde7 --- /dev/null +++ b/gl/tests/test-sys_select.c @@ -0,0 +1,30 @@ +/* Test of <sys/select.h> substitute. + Copyright (C) 2007-2008 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <bruno@clisp.org>, 2007. */ + +#include <config.h> + +#include <sys/select.h> + +/* Check that the 'struct timeval' type is defined. */ +struct timeval t1; + +int +main () +{ + return 0; +} diff --git a/gl/tests/test-sys_time.c b/gl/tests/test-sys_time.c new file mode 100644 index 0000000000..bae4108e07 --- /dev/null +++ b/gl/tests/test-sys_time.c @@ -0,0 +1,29 @@ +/* Test of <sys/time.h> substitute. + Copyright (C) 2007 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <bruno@clisp.org>, 2007. */ + +#include <config.h> + +#include <sys/time.h> + +struct timeval a; + +int +main () +{ + return 0; +} diff --git a/gl/tests/w32sock.h b/gl/tests/w32sock.h new file mode 100644 index 0000000000..0622985b81 --- /dev/null +++ b/gl/tests/w32sock.h @@ -0,0 +1,62 @@ +/* w32sock.h --- internal auxilliary functions for Windows socket functions + + Copyright (C) 2008 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Paolo Bonzini */ + +#include <errno.h> + +/* Get O_RDWR and O_BINARY. */ +#include <fcntl.h> + +/* Get _get_osfhandle() and _open_osfhandle(). */ +#include <io.h> + +#define FD_TO_SOCKET(fd) ((SOCKET) _get_osfhandle ((fd))) +#define SOCKET_TO_FD(fh) (_open_osfhandle ((long) (fh), O_RDWR | O_BINARY)) + +static inline void +set_winsock_errno (void) +{ + int err = WSAGetLastError (); + WSASetLastError (0); + + /* Map some WSAE* errors to the runtime library's error codes. */ + switch (err) + { + case WSA_INVALID_HANDLE: + errno = EBADF; + break; + case WSA_NOT_ENOUGH_MEMORY: + errno = ENOMEM; + break; + case WSA_INVALID_PARAMETER: + errno = EINVAL; + break; + case WSAEWOULDBLOCK: + errno = EWOULDBLOCK; + break; + case WSAENAMETOOLONG: + errno = ENAMETOOLONG; + break; + case WSAENOTEMPTY: + errno = ENOTEMPTY; + break; + default: + errno = (err > 10000 && err < 10025) ? err - 10000 : err; + break; + } +} diff --git a/gl/winsock-select.c b/gl/winsock-select.c new file mode 100644 index 0000000000..0b116cb1aa --- /dev/null +++ b/gl/winsock-select.c @@ -0,0 +1,423 @@ +/* Emulation for select(2) + Contributed by Paolo Bonzini. + + Copyright 2008 Free Software Foundation, Inc. + + This file is part of gnulib. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include <config.h> +#include <alloca.h> + +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ +#include <sys/types.h> +#include <stdbool.h> +#include <errno.h> +#include <limits.h> + +#include <winsock2.h> +#include <windows.h> +#include <io.h> +#include <stdio.h> +#include <conio.h> +#include <time.h> + +struct bitset { + unsigned char in[FD_SETSIZE / CHAR_BIT]; + unsigned char out[FD_SETSIZE / CHAR_BIT]; +}; + +/* Declare data structures for ntdll functions. */ +typedef struct _FILE_PIPE_LOCAL_INFORMATION { + ULONG NamedPipeType; + ULONG NamedPipeConfiguration; + ULONG MaximumInstances; + ULONG CurrentInstances; + ULONG InboundQuota; + ULONG ReadDataAvailable; + ULONG OutboundQuota; + ULONG WriteQuotaAvailable; + ULONG NamedPipeState; + ULONG NamedPipeEnd; +} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION; + +typedef struct _IO_STATUS_BLOCK +{ + union { + DWORD Status; + PVOID Pointer; + } u; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +typedef enum _FILE_INFORMATION_CLASS { + FilePipeLocalInformation = 24 +} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; + +typedef DWORD (WINAPI *PNtQueryInformationFile) + (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS); + +#ifndef PIPE_BUF +#define PIPE_BUF 512 +#endif + +/* Compute output fd_sets for libc descriptor FD (whose Win32 handle is H). */ + +static int +win32_poll_handle (HANDLE h, int fd, struct bitset *rbits, struct bitset *wbits, + struct bitset *xbits) +{ + BOOL read, write, except; + int i, ret; + INPUT_RECORD *irbuffer; + DWORD avail, nbuffer; + BOOL bRet; + IO_STATUS_BLOCK iosb; + FILE_PIPE_LOCAL_INFORMATION fpli; + static PNtQueryInformationFile NtQueryInformationFile; + static BOOL once_only; + + read = write = except = FALSE; + switch (GetFileType (h)) + { + case FILE_TYPE_DISK: + read = TRUE; + write = TRUE; + break; + + case FILE_TYPE_PIPE: + if (!once_only) + { + NtQueryInformationFile = (PNtQueryInformationFile) + GetProcAddress (GetModuleHandle ("ntdll.dll"), + "NtQueryInformationFile"); + once_only = TRUE; + } + + if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0) + { + if (avail) + read = TRUE; + } + + else + { + /* It was the write-end of the pipe. Check if it is writable. + If NtQueryInformationFile fails, optimistically assume the pipe is + writable. This could happen on Win9x, where NtQueryInformationFile + is not available, or if we inherit a pipe that doesn't permit + FILE_READ_ATTRIBUTES access on the write end (I think this should + not happen since WinXP SP2; WINE seems fine too). Otherwise, + ensure that enough space is available for atomic writes. */ + memset (&iosb, 0, sizeof (iosb)); + memset (&fpli, 0, sizeof (fpli)); + + if (!NtQueryInformationFile + || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli), + FilePipeLocalInformation) + || fpli.WriteQuotaAvailable >= PIPE_BUF + || (fpli.OutboundQuota < PIPE_BUF && + fpli.WriteQuotaAvailable == fpli.OutboundQuota)) + write = TRUE; + } + break; + + case FILE_TYPE_CHAR: + ret = WaitForSingleObject (h, 0); + write = TRUE; + if (ret == WAIT_OBJECT_0) + { + nbuffer = avail = 0; + bRet = GetNumberOfConsoleInputEvents (h, &nbuffer); + if (!bRet || nbuffer == 0) + except = TRUE; + + irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD)); + bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail); + if (!bRet || avail == 0) + except = TRUE; + + for (i = 0; i < avail; i++) + if (irbuffer[i].EventType == KEY_EVENT) + read = TRUE; + } + break; + + default: + ret = WaitForSingleObject (h, 0); + write = TRUE; + if (ret == WAIT_OBJECT_0) + read = TRUE; + + break; + } + + ret = 0; + if (read && (rbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1))))) + { + rbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1))); + ret++; + } + + if (write && (wbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1))))) + { + wbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1))); + ret++; + } + + if (except && (xbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1))))) + { + xbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1))); + ret++; + } + + return ret; +} + +int +rpl_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds, + struct timeval *timeout) +{ + static struct timeval tv0; + static HANDLE hEvent; + HANDLE h, handle_array[FD_SETSIZE + 2]; + fd_set handle_rfds, handle_wfds, handle_xfds; + struct bitset rbits, wbits, xbits; + unsigned char anyfds_in[FD_SETSIZE / CHAR_BIT]; + DWORD ret, wait_timeout, nhandles, nsock; + MSG msg; + int i, fd, rc; + + if (nfds > FD_SETSIZE) + nfds = FD_SETSIZE; + + if (!timeout) + wait_timeout = INFINITE; + else + { + wait_timeout = timeout->tv_sec + timeout->tv_usec / 1000; + + /* select is also used as a portable usleep. */ + if (!rfds && !wfds && !xfds) + { + Sleep (wait_timeout); + return 0; + } + } + + if (!hEvent) + hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); + + handle_array[0] = hEvent; + nhandles = 1; + nsock = 0; + + /* Copy descriptors to bitsets. */ + memset (&rbits, 0, sizeof (rbits)); + memset (&wbits, 0, sizeof (wbits)); + memset (&xbits, 0, sizeof (xbits)); + memset (anyfds_in, 0, sizeof (anyfds_in)); + if (rfds) + for (i = 0; i < rfds->fd_count; i++) + { + fd = rfds->fd_array[i]; + rbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); + anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); + } + else + rfds = (fd_set *) alloca (sizeof (fd_set)); + + if (wfds) + for (i = 0; i < wfds->fd_count; i++) + { + fd = wfds->fd_array[i]; + wbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); + anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); + } + else + wfds = (fd_set *) alloca (sizeof (fd_set)); + + if (xfds) + for (i = 0; i < xfds->fd_count; i++) + { + fd = xfds->fd_array[i]; + xbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); + anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); + } + else + xfds = (fd_set *) alloca (sizeof (fd_set)); + + /* Zero all the fd_sets, including the application's. */ + FD_ZERO (rfds); + FD_ZERO (wfds); + FD_ZERO (xfds); + FD_ZERO (&handle_rfds); + FD_ZERO (&handle_wfds); + FD_ZERO (&handle_xfds); + + /* Classify handles. Create fd sets for sockets, poll the others. */ + for (i = 0; i < nfds; i++) + { + WSANETWORKEVENTS ev; + + if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0) + continue; + + h = (HANDLE) _get_osfhandle (i); + if (!h) + { + errno = EBADF; + return -1; + } + + /* Under Wine, it seems that getsockopt returns 0 for pipes too. + WSAEnumNetworkEvents instead distinguishes the two correctly. */ + ev.lNetworkEvents = 0xDEADBEEF; + WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev); + if (ev.lNetworkEvents != 0xDEADBEEF) + { + int requested = FD_CLOSE; + + /* See above; socket handles are mapped onto select, but we + need to map descriptors to handles. */ + if (rbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) + { + requested |= FD_READ | FD_ACCEPT; + FD_SET ((SOCKET) h, rfds); + FD_SET ((SOCKET) h, &handle_rfds); + } + if (wbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) + { + requested |= FD_WRITE | FD_CONNECT; + FD_SET ((SOCKET) h, wfds); + FD_SET ((SOCKET) h, &handle_wfds); + } + if (xbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) + { + requested |= FD_OOB; + FD_SET ((SOCKET) h, xfds); + FD_SET ((SOCKET) h, &handle_xfds); + } + + WSAEventSelect ((SOCKET) h, hEvent, requested); + nsock++; + } + else + { + handle_array[nhandles++] = h; + + /* Poll now. If we get an event, do not wait below. */ + if (wait_timeout != 0 + && win32_poll_handle (h, i, &rbits, &wbits, &xbits)) + wait_timeout = 0; + } + } + + if (wait_timeout == 0 || nsock == 0) + rc = 0; + else + { + /* See if we need to wait in the loop below. If any select is ready, + do MsgWaitForMultipleObjects anyway to dispatch messages, but + no need to call select again. */ + rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0); + if (rc == 0) + { + /* Restore the fd_sets for the other select we do below. */ + memcpy (&handle_rfds, rfds, sizeof (fd_set)); + memcpy (&handle_wfds, wfds, sizeof (fd_set)); + memcpy (&handle_xfds, xfds, sizeof (fd_set)); + } + else + wait_timeout = 0; + } + + for (;;) + { + ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE, + wait_timeout, QS_ALLINPUT); + + if (ret == WAIT_OBJECT_0 + nhandles) + { + /* new input of some other kind */ + BOOL bRet; + while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + } + else + break; + } + + /* If we haven't done it yet, check the status of the sockets. */ + if (rc == 0 && nsock > 0) + rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0); + + /* Now fill in the results. */ + FD_ZERO (rfds); + FD_ZERO (wfds); + FD_ZERO (xfds); + + /* Place a sentinel at the end of the array. */ + handle_array[nhandles] = NULL; + nhandles = 1; + for (i = 0; i < nfds; i++) + { + if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0) + continue; + + h = (HANDLE) _get_osfhandle (i); + if (h != handle_array[nhandles]) + { + /* Perform handle->descriptor mapping. Don't update rc, as these + results are counted in the return value of Winsock's select. */ + WSAEventSelect ((SOCKET) h, NULL, 0); + if (FD_ISSET (h, &handle_rfds)) + FD_SET (i, rfds); + if (FD_ISSET (h, &handle_wfds)) + FD_SET (i, wfds); + if (FD_ISSET (h, &handle_xfds)) + FD_SET (i, xfds); + } + else + { + /* Not a socket. */ + nhandles++; + win32_poll_handle (h, i, &rbits, &wbits, &xbits); + if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) + { + rc++; + FD_SET (i, rfds); + } + if (wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) + { + rc++; + FD_SET (i, wfds); + } + if (xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) + { + rc++; + FD_SET (i, xfds); + } + } + } + + return rc; +} + +#endif /* Native Win32. */ |