1 /* Copyright (c) 2001 John E. Davis
2  * This file is part of the S-Lang library.
3  *
4  * You may distribute under the terms of either the GNU General Public
5  * License or the Perl Artistic License.
6  */
7 #include <stdio.h>
8 #include <slang.h>
9 
10 #include <sys/time.h>
11 #include <sys/types.h>
12 #include <unistd.h>
13 #include <string.h>
14 #include <errno.h>
15 
16 SLANG_MODULE(select);
17 
pop_fd_set(SLang_Array_Type ** ats,fd_set ** fd_set_p,fd_set * fd_set_buf,int * max_n)18 static int pop_fd_set (SLang_Array_Type **ats,
19 		       fd_set **fd_set_p, fd_set *fd_set_buf,
20 		       int *max_n)
21 {
22    unsigned int num, i;
23    SLang_Array_Type *at;
24    SLFile_FD_Type **f;
25 
26    *ats = NULL;
27    *fd_set_p = NULL;
28 
29    if (SLang_peek_at_stack () == SLANG_NULL_TYPE)
30      return SLang_pop_null ();
31 
32    if (-1 == SLang_pop_array_of_type (&at, SLANG_FILE_FD_TYPE))
33      return -1;
34 
35    FD_ZERO(fd_set_buf);
36    *fd_set_p = fd_set_buf;
37 
38    *ats = at;
39    num = at->num_elements;
40    f = (SLFile_FD_Type **) at->data;
41 
42    for (i = 0; i < num; i++)
43      {
44 	int fd;
45 
46 	if (-1 == SLfile_get_fd (f[i], &fd))
47 	  continue;
48 
49 	if (fd > *max_n)
50 	  *max_n = fd;
51 
52 	FD_SET(fd, fd_set_buf);
53      }
54 
55    return 0;
56 }
57 
do_fdisset(int nready,SLang_Array_Type * fds,fd_set * fdset)58 static SLang_Array_Type *do_fdisset (int nready, SLang_Array_Type *fds, fd_set *fdset)
59 {
60    SLang_Array_Type *at;
61    unsigned int i, num;
62    SLFile_FD_Type **f;
63 
64    if (fds == NULL)
65      nready = 0;
66 
67    if (nready)
68      {
69 	nready = 0;
70 	num = fds->num_elements;
71 	f = (SLFile_FD_Type **) fds->data;
72 	for (i = 0; i < num; i++)
73 	  {
74 	     int fd;
75 
76 	     if (-1 == SLfile_get_fd (f[i], &fd))
77 	       continue;
78 
79 	     if (FD_ISSET(fd, fdset))
80 	       nready++;
81 	  }
82      }
83 
84    at = SLang_create_array (SLANG_INT_TYPE, 0, NULL, &nready, 1);
85    if (at == NULL)
86      return NULL;
87 
88    if (nready)
89      {
90 	int *indx = (int *) at->data;
91 	f = (SLFile_FD_Type **) fds->data;
92 	num = fds->num_elements;
93 	for (i = 0; i < num; i++)
94 	  {
95 	     int fd;
96 
97 	     if (-1 == SLfile_get_fd (f[i], &fd))
98 	       continue;
99 
100 	     if (FD_ISSET(fd, fdset))
101 	       *indx++ = (int) i;
102 	  }
103      }
104 
105    return at;
106 }
107 
push_select_struct(int num,SLang_Array_Type * at_read,SLang_Array_Type * at_write,SLang_Array_Type * at_except,fd_set * readfs,fd_set * writefds,fd_set * exceptfds)108 static int push_select_struct (int num,
109 			       SLang_Array_Type *at_read,
110 			       SLang_Array_Type *at_write,
111 			       SLang_Array_Type *at_except,
112 			       fd_set *readfs, fd_set *writefds, fd_set *exceptfds)
113 {
114    char *field_names [4];
115    unsigned char field_types[4];
116    VOID_STAR field_values [4];
117    SLang_Array_Type *iread, *iwrite, *iexcept;
118 
119    iread = iwrite = iexcept = NULL;
120 
121    field_names[0] = "nready";
122    field_names[1] = "iread";
123    field_names[2] = "iwrite";
124    field_names[3] = "iexcept";
125    field_types[0] = SLANG_INT_TYPE;
126    field_types[1] = SLANG_ARRAY_TYPE;
127    field_types[2] = SLANG_ARRAY_TYPE;
128    field_types[3] = SLANG_ARRAY_TYPE;
129    field_values[0] = &num;
130 
131    if ((NULL == (iread = do_fdisset (num, at_read, readfs)))
132        || (NULL == (iwrite = do_fdisset (num, at_write, writefds)))
133        || (NULL == (iexcept = do_fdisset (num, at_except, exceptfds))))
134      {
135 	SLang_free_array (iread);
136 	SLang_free_array (iwrite);
137 	return -1;
138      }
139 
140    field_values[1] = &iread;
141    field_values[2] = &iwrite;
142    field_values[3] = &iexcept;
143 
144    /* Note: This function call pushes the struct and frees it upon error. */
145    return SLstruct_create_struct (4, field_names, field_types, field_values);
146 }
147 
148 
149 /* Usage: Struct_Type select (R[],W[],E[],TIME) */
150 
select_intrin(double * secsp)151 static void select_intrin (double *secsp)
152 {
153    SLang_Array_Type *at_read, *at_write, *at_except;
154    fd_set readfs_buf, writefds_buf, exceptfds_buf;
155    fd_set readfs_save_buf, writefds_save_buf, exceptfds_save_buf;
156    fd_set *readfs, *writefds, *exceptfds;
157    struct timeval tv, *tv_ptr;
158    double secs;
159    int ret, n;
160 
161    secs = *secsp;
162    if (secs < 0.0) tv_ptr = NULL;
163    else
164      {
165 	tv.tv_sec = (unsigned long) secs;
166 	tv.tv_usec = (unsigned long) ((secs - tv.tv_sec) * 1e6);
167 	tv_ptr = &tv;
168      }
169 
170    n = 0;
171    if (-1 == pop_fd_set (&at_except, &exceptfds, &exceptfds_buf, &n))
172      return;
173    if (-1 == pop_fd_set (&at_write, &writefds, &writefds_buf, &n))
174      {
175 	SLang_free_array (at_except);
176 	return;
177      }
178    if (-1 == pop_fd_set (&at_read, &readfs, &readfs_buf, &n))
179      goto free_return;
180 
181    readfs_save_buf = readfs_buf;
182    writefds_save_buf = writefds_buf;
183    exceptfds_save_buf = exceptfds_buf;
184 
185    n += 1;
186    while (-1 == (ret = select (n, readfs, writefds, exceptfds, tv_ptr)))
187      {
188 #ifdef EINTR
189 	if (errno == EINTR)
190 	  {
191 	     readfs_buf = readfs_save_buf;
192 	     writefds_buf = writefds_save_buf;
193 	     exceptfds_buf = exceptfds_save_buf;
194 	     continue;
195 	  }
196 #endif
197 	(void) SLerrno_set_errno (errno);
198 	break;
199      }
200 
201    if (ret == -1)
202      (void) SLang_push_null ();
203    else
204      (void) push_select_struct (ret, at_read, at_write, at_except,
205 				readfs, writefds, exceptfds);
206 
207 
208    free_return:
209    SLang_free_array (at_read);
210    SLang_free_array (at_write);
211    SLang_free_array (at_except);
212 }
213 
214 static SLang_Intrin_Fun_Type Select_Intrinsics [] =
215 {
216    MAKE_INTRINSIC_1("select", select_intrin, SLANG_VOID_TYPE, SLANG_DOUBLE_TYPE),
217    SLANG_END_INTRIN_FUN_TABLE
218 };
219 
220 
init_select_module_ns(char * ns_name)221 int init_select_module_ns (char *ns_name)
222 {
223    SLang_NameSpace_Type *ns;
224 
225    ns = SLns_create_namespace (ns_name);
226    if (ns == NULL)
227      return -1;
228 
229    if (-1 == SLns_add_intrin_fun_table (ns, Select_Intrinsics, "__SELECT__"))
230      return -1;
231 
232    return 0;
233 }
234 
235 /* This function is optional */
deinit_select_module(void)236 void deinit_select_module (void)
237 {
238 }
239