|
1 // zselect.c - builtin support for select system call |
|
2 // |
|
3 // © Portions copyright (c) 2007 Symbian Software Ltd. All rights reserved. |
|
4 // |
|
5 /* |
|
6 * This file is part of zsh, the Z shell. |
|
7 * |
|
8 * Copyright (c) 1998-2001 Peter Stephenson |
|
9 * All rights reserved. |
|
10 * |
|
11 * Permission is hereby granted, without written agreement and without |
|
12 * license or royalty fees, to use, copy, modify, and distribute this |
|
13 * software and to distribute modified versions of this software for any |
|
14 * purpose, provided that the above copyright notice and the following |
|
15 * two paragraphs appear in all copies of this software. |
|
16 * |
|
17 * In no event shall Peter Stephenson or the Zsh Development |
|
18 * Group be liable to any party for direct, indirect, special, incidental, |
|
19 * or consequential damages arising out of the use of this software and |
|
20 * its documentation, even if Peter Stephenson, and the Zsh |
|
21 * Development Group have been advised of the possibility of such damage. |
|
22 * |
|
23 * Peter Stephenson and the Zsh Development Group specifically |
|
24 * disclaim any warranties, including, but not limited to, the implied |
|
25 * warranties of merchantability and fitness for a particular purpose. The |
|
26 * software provided hereunder is on an "as is" basis, and Peter Stephenson |
|
27 * and the Zsh Development Group have no obligation to provide maintenance, |
|
28 * support, updates, enhancements, or modifications. |
|
29 * |
|
30 */ |
|
31 #include "zselect.mdh" |
|
32 #include "zselect.pro" |
|
33 |
|
34 #ifdef __SYMBIAN32__ |
|
35 #ifdef __WINSCW__ |
|
36 #pragma warn_unusedarg off |
|
37 #endif//__WINSCW__ |
|
38 #endif//__SYMBIAN32__ |
|
39 |
|
40 /* Helper functions */ |
|
41 |
|
42 /* |
|
43 * Handle an fd by adding it to the current fd_set. |
|
44 * Return 1 for error (after printing a message), 0 for OK. |
|
45 */ |
|
46 static int |
|
47 handle_digits(char *nam, char *argptr, fd_set *fdset, int *fdmax) |
|
48 { |
|
49 int fd; |
|
50 char *endptr; |
|
51 |
|
52 if (!isdigit(STOUC(*argptr))) { |
|
53 zwarnnam(nam, "expecting file descriptor: %s", argptr, 0); |
|
54 return 1; |
|
55 } |
|
56 fd = (int)zstrtol(argptr, &endptr, 10); |
|
57 if (*endptr) { |
|
58 zwarnnam(nam, "garbage after file descriptor: %s", endptr, 0); |
|
59 return 1; |
|
60 } |
|
61 |
|
62 FD_SET(fd, fdset); |
|
63 if (fd+1 > *fdmax) |
|
64 *fdmax = fd+1; |
|
65 return 0; |
|
66 } |
|
67 |
|
68 /* The builtin itself */ |
|
69 |
|
70 /**/ |
|
71 static int |
|
72 bin_zselect(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) |
|
73 { |
|
74 #ifdef HAVE_SELECT |
|
75 int i, fd, fdsetind = 0, fdmax = 0, fdcount; |
|
76 fd_set fdset[3]; |
|
77 const char fdchar[3] = "rwe"; |
|
78 struct timeval tv, *tvptr = NULL; |
|
79 char *outarray = "reply", **outdata, **outptr; |
|
80 char *outhash = NULL; |
|
81 LinkList fdlist; |
|
82 |
|
83 for (i = 0; i < 3; i++) |
|
84 FD_ZERO(fdset+i); |
|
85 |
|
86 for (; *args; args++) { |
|
87 char *argptr = *args, *endptr; |
|
88 zlong tempnum; |
|
89 if (*argptr == '-') { |
|
90 for (argptr++; *argptr; argptr++) { |
|
91 switch (*argptr) { |
|
92 /* |
|
93 * Array name for reply, if not $reply. |
|
94 * This gets set to e.g. `-r 0 -w 1' if 0 is ready |
|
95 * for reading and 1 is ready for writing. |
|
96 */ |
|
97 case 'a': |
|
98 case 'A': |
|
99 i = *argptr; |
|
100 if (argptr[1]) |
|
101 argptr++; |
|
102 else if (args[1]) { |
|
103 argptr = *++args; |
|
104 } else { |
|
105 zwarnnam(nam, "argument expected after -%c", NULL, |
|
106 *argptr); |
|
107 return 1; |
|
108 } |
|
109 if (idigit(*argptr) || !isident(argptr)) { |
|
110 zwarnnam(nam, "invalid array name: %s", argptr, 0); |
|
111 return 1; |
|
112 } |
|
113 if (i == 'a') |
|
114 outarray = argptr; |
|
115 else |
|
116 outhash = argptr; |
|
117 /* set argptr to next to last char because of increment */ |
|
118 while (argptr[1]) |
|
119 argptr++; |
|
120 break; |
|
121 |
|
122 /* Following numbers indicate fd's for reading */ |
|
123 case 'r': |
|
124 fdsetind = 0; |
|
125 break; |
|
126 |
|
127 /* Following numbers indicate fd's for writing */ |
|
128 case 'w': |
|
129 fdsetind = 1; |
|
130 break; |
|
131 |
|
132 /* Following numbers indicate fd's for errors */ |
|
133 case 'e': |
|
134 fdsetind = 2; |
|
135 break; |
|
136 |
|
137 /* |
|
138 * Get a timeout value in hundredths of a second |
|
139 * (same units as KEYTIMEOUT). 0 means just poll. |
|
140 * If not given, blocks indefinitely. |
|
141 */ |
|
142 case 't': |
|
143 if (argptr[1]) |
|
144 argptr++; |
|
145 else if (args[1]) { |
|
146 argptr = *++args; |
|
147 } else { |
|
148 zwarnnam(nam, "argument expected after -%c", NULL, |
|
149 *argptr); |
|
150 return 1; |
|
151 } |
|
152 if (!idigit(*argptr)) { |
|
153 zwarnnam(nam, "number expected after -t", NULL, 0); |
|
154 return 1; |
|
155 } |
|
156 tempnum = zstrtol(argptr, &endptr, 10); |
|
157 if (*endptr) { |
|
158 zwarnnam(nam, "garbage after -t argument: %s", |
|
159 endptr, 0); |
|
160 return 1; |
|
161 } |
|
162 /* timevalue now active */ |
|
163 tvptr = &tv; |
|
164 tv.tv_sec = (long)(tempnum / 100); |
|
165 tv.tv_usec = (long)(tempnum % 100) * 10000L; |
|
166 |
|
167 /* remember argptr is incremented at end of loop */ |
|
168 argptr = endptr - 1; |
|
169 break; |
|
170 |
|
171 /* Digits following option without arguments are fd's. */ |
|
172 default: |
|
173 if (handle_digits(nam, argptr, fdset+fdsetind, |
|
174 &fdmax)) |
|
175 return 1; |
|
176 } |
|
177 } |
|
178 } else if (handle_digits(nam, argptr, fdset+fdsetind, &fdmax)) |
|
179 return 1; |
|
180 } |
|
181 |
|
182 errno = 0; |
|
183 do { |
|
184 i = select(fdmax, (SELECT_ARG_2_T)fdset, (SELECT_ARG_2_T)(fdset+1), |
|
185 (SELECT_ARG_2_T)(fdset+2), tvptr); |
|
186 } while (i < 0 && errno == EINTR && !errflag); |
|
187 |
|
188 if (i <= 0) { |
|
189 if (i < 0) |
|
190 zwarnnam(nam, "error on select: %e", NULL, errno); |
|
191 /* else no fd's set. Presumably a timeout. */ |
|
192 return 1; |
|
193 } |
|
194 |
|
195 /* |
|
196 * Make a linked list of all file descriptors which are ready. |
|
197 * These go into an array preceded by -r, -w or -e for read, write, |
|
198 * error as appropriate. Typically there will only be one set |
|
199 * so this looks rather like overkill. |
|
200 */ |
|
201 fdlist = znewlinklist(); |
|
202 for (i = 0; i < 3; i++) { |
|
203 int doneit = 0; |
|
204 for (fd = 0; fd < fdmax; fd++) { |
|
205 if (FD_ISSET(fd, fdset+i)) { |
|
206 char buf[BDIGBUFSIZE]; |
|
207 if (outhash) { |
|
208 /* |
|
209 * Key/value pairs; keys are fd's (as strings), |
|
210 * value is a (possibly improper) subset of "rwe". |
|
211 */ |
|
212 LinkNode nptr; |
|
213 int found = 0; |
|
214 |
|
215 convbase(buf, fd, 10); |
|
216 for (nptr = firstnode(fdlist); nptr; |
|
217 nptr = nextnode(nextnode(nptr))) { |
|
218 if (!strcmp((char *)getdata(nptr), buf)) { |
|
219 /* Already there, add new character. */ |
|
220 void **dataptr = getaddrdata(nextnode(nptr)); |
|
221 char *data = (char *)*dataptr, *ptr; |
|
222 found = 1; |
|
223 if (!strchr(data, fdchar[i])) { |
|
224 strcpy(buf, data); |
|
225 for (ptr = buf; *ptr; ptr++) |
|
226 ; |
|
227 *ptr++ = fdchar[i]; |
|
228 *ptr = '\0'; |
|
229 zsfree(data); |
|
230 *dataptr = ztrdup(buf); |
|
231 } |
|
232 break; |
|
233 } |
|
234 } |
|
235 if (!found) { |
|
236 /* Add new key/value pair. */ |
|
237 zaddlinknode(fdlist, ztrdup(buf)); |
|
238 buf[0] = fdchar[i]; |
|
239 buf[1] = '\0'; |
|
240 zaddlinknode(fdlist, ztrdup(buf)); |
|
241 } |
|
242 } else { |
|
243 /* List of fd's preceded by -r, -w, -e. */ |
|
244 if (!doneit) { |
|
245 buf[0] = '-'; |
|
246 buf[1] = fdchar[i]; |
|
247 buf[2] = 0; |
|
248 zaddlinknode(fdlist, ztrdup(buf)); |
|
249 doneit = 1; |
|
250 } |
|
251 convbase(buf, fd, 10); |
|
252 zaddlinknode(fdlist, ztrdup(buf)); |
|
253 } |
|
254 } |
|
255 } |
|
256 } |
|
257 |
|
258 /* convert list to array */ |
|
259 fdcount = countlinknodes(fdlist); |
|
260 outptr = outdata = (char **)zalloc((fdcount+1)*sizeof(char *)); |
|
261 while (nonempty(fdlist)) |
|
262 *outptr++ = getlinknode(fdlist); |
|
263 *outptr = '\0'; |
|
264 /* and store in array parameter */ |
|
265 if (outhash) |
|
266 sethparam(outhash, outdata); |
|
267 else |
|
268 setaparam(outarray, outdata); |
|
269 freelinklist(fdlist, NULL); |
|
270 |
|
271 return 0; |
|
272 #else |
|
273 /* TODO: use poll */ |
|
274 zerrnam(nam, "your system does not implement the select system call.", |
|
275 NULL, 0); |
|
276 return 2; |
|
277 #endif |
|
278 } |
|
279 |
|
280 static struct builtin bintab[] = { |
|
281 BUILTIN("zselect", 0, bin_zselect, 0, -1, 0, NULL, NULL), |
|
282 }; |
|
283 |
|
284 /* The load/unload routines required by the zsh library interface */ |
|
285 |
|
286 /**/ |
|
287 int |
|
288 setup_(UNUSED(Module m)) |
|
289 { |
|
290 return 0; |
|
291 } |
|
292 |
|
293 /**/ |
|
294 int |
|
295 boot_(Module m) |
|
296 { |
|
297 return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); |
|
298 } |
|
299 |
|
300 |
|
301 /**/ |
|
302 int |
|
303 cleanup_(UNUSED(Module m)) |
|
304 { |
|
305 deletebuiltins("zselect", bintab, sizeof(bintab)/sizeof(*bintab)); |
|
306 return 0; |
|
307 } |
|
308 |
|
309 /**/ |
|
310 int |
|
311 finish_(UNUSED(Module m)) |
|
312 { |
|
313 return 0; |
|
314 } |